BZOJ1112: [POI2008]砖块Klo
【传送门:BZOJ1112】
简要题意:
给出n个数,每一次操作可以使得一个数增加1或者减少1
求出能得到连续k个相等的数的最少操作次数
题解:
师兄模拟赛的题
主席树求中位数就好了,求的时候顺便求次数就可以了
然而,赛中,师兄,卡空间!!!
才给我们开40M的空间,结果我的代码是44M。。。
光荣爆零
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; struct trnode { int lc,rc,c;LL sum; }tr[1770000];int trlen,rt[110000]; int a[110000],s[110000],n; int LS(int d) { int l=1,r=n,ans; while(l<=r) { int mid=(l+r)/2; if(s[mid]<=d) { ans=mid; l=mid+1; } else r=mid-1; } return ans; } void Link(int &u,int l,int r,int p) { if(u==0) u=++trlen; tr[u].c++;tr[u].sum+=s[p]; if(l==r) return ; int mid=(l+r)/2; if(p<=mid) Link(tr[u].lc,l,mid,p); else Link(tr[u].rc,mid+1,r,p); } void Merge(int &u1,int u2) { if(u1==0){u1=u2;return ;} if(u2==0) return ; tr[u1].c+=tr[u2].c; tr[u1].sum+=tr[u2].sum; Merge(tr[u1].lc,tr[u2].lc); Merge(tr[u1].rc,tr[u2].rc); } LL ll,rr;int cl,cr; LL findmid(int u1,int u2,int l,int r,int p) { if(l==r) return LL(cl)*s[l]-ll+rr-LL(cr)*s[l]; int lc=tr[tr[u1].lc].c-tr[tr[u2].lc].c,rc=tr[tr[u1].rc].c-tr[tr[u2].rc].c; LL ls=tr[tr[u1].lc].sum-tr[tr[u2].lc].sum,rs=tr[tr[u1].rc].sum-tr[tr[u2].rc].sum; int mid=(l+r)/2; if(p<=lc) { cr+=rc; rr+=rs; return findmid(tr[u1].lc,tr[u2].lc,l,mid,p); } else { cl+=lc; ll+=ls; return findmid(tr[u1].rc,tr[u2].rc,mid+1,r,p-lc); } } int main() { int k; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&a[i]),s[i]=a[i]; sort(s+1,s+n+1); trlen=0;memset(rt,0,sizeof(rt)); for(int i=1;i<=n;i++) { Link(rt[i],1,n,LS(a[i])); Merge(rt[i],rt[i-1]); } LL ans=1LL<<63-1; for(int i=k;i<=n;i++) { ll=rr=cl=cr=0; ans=min(ans,findmid(rt[i],rt[i-k],1,n,(k+1)/2)); } printf("%lld\n",ans); return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚