Codeforces 786C Till I Collapse

看到题目第一反应:出题人好善良,出个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 } 

 

posted @ 2017-03-27 16:52  汪立超  阅读(367)  评论(0编辑  收藏  举报