看到题目第一反应:出题人好善良,出个1e5放两个log过
然后真的过了
(๑• . •๑)
题解:
首先每次贪心地找最远能延伸到的地方肯定是对的——显然法可知,多拿走一个对后面的选取只有有利的影响
这里有一个简单的证明:n+n/2+n/3+...+n/n=O(nlogn)——不要问我怎么证,我真的不会(话说这个应该是常识吧)
然后只要兹磁logn查询一个只有i个颜色的区间就可以了
开一棵主席树记录从[i,n]的点中有贡献的位置,每次查询一个最后的位置k使a1+a2+...+ak≤i即可=>也就是第i+1个1的位置-1
考虑这棵主席树如何维护:从尾往前跑,每次只会有1个点(新增点)出现新的贡献,最多1个点(最近的同色点)的贡献被抹杀
没了
1 #include <bits/stdc++.h> 2 #define mid (l+r>>1) 3 using namespace std; 4 int N,n,tem; 5 int root[5000000],tr[5000000],ls[5000000],rs[5000000],a[5000000],last[5000000]; 6 void add(int now,int acc,int l,int r,int x,int y) 7 { 8 if(l==r) 9 { 10 tr[now]=tr[acc]+y; 11 ls[now]=rs[now]=0; 12 return; 13 } 14 if(x<=mid) 15 rs[now]=rs[acc],add(ls[now]=++N,ls[acc],l,mid,x,y), 16 tr[now]=tr[rs[now]]+tr[ls[now]]; 17 else 18 ls[now]=ls[acc],add(rs[now]=++N,rs[acc],mid+1,r,x,y), 19 tr[now]=tr[rs[now]]+tr[ls[now]]; 20 } 21 int main() 22 { 23 scanf("%d",&n); 24 for(int i=1;i<=n;i++) 25 scanf("%d",&a[i]); 26 root[n+1]=N=1; 27 for(int i=1;i<=n;i++) 28 last[i]=0; 29 for(int i=n;i>=1;--i) 30 { 31 add(tem=++N,root[i+1],1,n,i,1); 32 if(last[a[i]]) 33 add(root[i]=++N,tem,1,n,last[a[i]],-1); 34 else 35 root[i]=tem; 36 last[a[i]]=i; 37 } 38 for(int i=1;i<=n;i++) 39 { 40 int po=1,ret=0,rest=i; 41 for(int now,l,r;tr[root[po]]>rest;++ret,po=l,rest=i) 42 for(now=root[po],l=1,r=n;l<r;) 43 (tr[ls[now]]<=rest)?(rest-=tr[ls[now]],now=rs[now],l=mid+1):(now=ls[now],r=mid); 44 printf("%d ",ret+(bool)tr[root[po]]); 45 } 46 return 0; 47 }