Codeforces 786C Till I Collapse(树状数组+扫描线+倍增)
【题目链接】 http://codeforces.com/contest/786/problem/C
【题目大意】
给出一个数列,问对于不同的k,将区间划分为几个,
每个区间出现不同元素个数不超过k时最少的区间划分数量。
【题解】
我们可以用树状数组+扫描线求出一个区间不同元素的数量,
我们记录每一个位置上下一个相同相同元素的位置,当扫描线扫过当前点时
我们消除这个点的影响,并在其下个出现的位置进行更新,
这样就能求出固定左端点不同右端点情况下不同区间内不同元素的数量,
这个题我们可以用倍增找出对于每个k在扫描到的当前点,
下一个满足限制条件的最远点在哪里,然后在那里保存下这个k,
当扫描到那里的时候继续计算,查询的次数就是区间的数目。
【代码】
#include <cstdio> #include <algorithm> #include <vector> using namespace std; const int N=1000100; vector<int> f[N]; int n,m,a[N],c[N],pre[N],nxt[N],ans[N]; void add(int x,int val){while(x<=n+1)c[x]+=val,x+=x&-x;} int find(int x){ int p=0; for(int i=20;i>=0;i--){ if(p+(1<<i)<=n+1&&c[p+(1<<i)]<x){ x-=c[p+(1<<i)]; p+=(1<<i); } }return p+1; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=n+1;i++)pre[i]=n+1; for(int i=n+1;i>=1;i--){ nxt[i]=pre[a[i]]; pre[a[i]]=i; } for(int i=1;i<=n+1;i++)add(pre[i],1),f[1].push_back(i); for(int i=1;i<=n;i++){ int w=f[i].size(); for(int j=0;j<w;j++){ int d=f[i][j]; int pos=find(d+1); ans[d]++; f[pos].push_back(d); }add(i,-1),add(nxt[i],1); }for(int i=1;i<=n;i++)printf("%d%c",ans[i],i==n?'\n':' '); return 0; }
愿你出走半生,归来仍是少年