传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1112
题目大意:N柱砖,希望有连续K柱的高度是一样的. 你可以选择以下两个动作 1:从某柱砖的顶端拿一块砖出来,丢掉不要了. 2:从仓库中拿出一块砖,放到另一柱.仓库无限大. 现在希望用最小次数的动 作完成任务.
题解:我们看看n是106于是我们可以枚举k,是O(n)的,那么看看时间复杂度,就需要一个logN的算法来删除一个数,加入一个数,寻找第k个数,于是平衡树即可!时间复杂度:NlogN;
实际上刚开始想的是二分次数+二分长度,想想应该也是可行的,只不过在答案验证上,可以自己想想,反正我不知道!
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm> 5 #include<cmath> 6 #define maxn 100005 7 #define ll long long 8 #define linf 9223372036854775807LL 9 using namespace std; 10 int n,k,root,mid,mmid,sz; 11 ll tmp; 12 ll sum1,sum2,ans; 13 int a[maxn]; 14 int ls[maxn],rs[maxn],size[maxn],w[maxn],rnd[maxn]; 15 ll sum[maxn],v[maxn]; 16 int read() 17 { 18 int x=0; char ch; bool bo=0; 19 while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') bo=1; 20 while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); 21 if (bo) return -x; return x; 22 } 23 void updata(int k) 24 { 25 sum[k]=sum[ls[k]]+sum[rs[k]]+w[k]*v[k]; 26 size[k]=size[ls[k]]+size[rs[k]]+w[k]; 27 } 28 void rturn(int &k) 29 { 30 int t=ls[k]; ls[k]=rs[t]; rs[t]=k; updata(k); updata(t); k=t; return; 31 } 32 void lturn(int &k) 33 { 34 int t=rs[k]; rs[k]=ls[t]; ls[t]=k; updata(k); updata(t); k=t; return; 35 } 36 void ins(int &k,int val) 37 { 38 if (!k) 39 { 40 k=++sz; size[k]=w[k]=1; v[k]=sum[k]=val; rnd[k]=rand(); return ; 41 } 42 size[k]++; sum[k]+=val; 43 if (val==v[k]) w[k]++; 44 if (v[k]>val){ins(ls[k],val); if (rnd[ls[k]]>rnd[k]) rturn(k);} 45 if (v[k]<val){ins(rs[k],val); if (rnd[rs[k]]>rnd[k]) lturn(k);} 46 updata(k); 47 } 48 void del(int &k,int val) 49 { 50 if (!k) return ; 51 if (v[k]==val) 52 { 53 if (w[k]) 54 { 55 w[k]--; size[k]--; sum[k]-=val; return ; 56 } 57 if (ls[k]*rs[k]==0) k=ls[k]+rs[k]; 58 else 59 { 60 if (rnd[ls[k]]>rnd[rs[k]]) {rturn(k); del(k,val);} 61 else {lturn(k); del(k,val);} 62 } 63 } 64 else if (v[k]>val) {sum[k]-=val; size[k]--; del(ls[k],val);} 65 else {sum[k]-=val; size[k]--; del(rs[k],val);} 66 } 67 void init() 68 { 69 n=read(); k=read(); mmid=k/2+1; ans=linf; 70 for(int i=1; i<=n; i++) a[i]=read(); 71 } 72 void find(int k,int rank) 73 { 74 if(!k)return; 75 if(rank>size[ls[k]]&&rank<=size[ls[k]]+w[k]) 76 { 77 sum1+=(sum[ls[k]]+(rank-size[ls[k]]-1)*v[k]); 78 sum2+=(sum[rs[k]]+(size[ls[k]]+w[k]-rank)*v[k]); 79 tmp=v[k]; 80 } 81 else if(rank<=size[ls[k]]) 82 { 83 sum2+=(v[k]*w[k]+sum[rs[k]]); 84 find(ls[k],rank); 85 } 86 else 87 { 88 sum1+=(v[k]*w[k]+sum[ls[k]]); 89 find(rs[k],rank-size[ls[k]]-w[k]); 90 } 91 } 92 void getans() 93 { 94 sum1=sum2=0; 95 find(root,mmid); 96 ll sum=(mmid-1)*tmp-sum1+sum2-(k-mmid)*tmp; 97 if(sum<ans)ans=sum; 98 } 99 void solve() 100 { 101 root=0; 102 for (int i=1; i<=k; i++) ins(root,a[i]); 103 getans(); 104 for (int i=k+1; i<=n; i++) 105 { 106 del(root,a[i-k]); ins(root,a[i]); 107 getans(); 108 } 109 printf("%lld ",ans); 110 } 111 int main() 112 { 113 init(); 114 solve(); 115 }