Codeforces 787E Till I Collapse 主席樹+二分
阿新 • • 發佈:2019-02-16
題意:n個數,一個group為下標連續的數&&不同的數最多隻能有k個,問k=1~n時的最小分組? n,ai<=1e5
為了對每個固定的L,找到最後一個滿足的r 使得[L,r]內不同的數正好為k。
主席樹維護字首i時,從下標L=1開始 二分找到最後一個滿足k的r 令L=pos+1繼續二分,每次L最至少前進k格(n/1+n/2+...n/n=nlogn) 則複雜度為O(nlogn*logn*logn) TLE
利用主席樹維護字尾i中下標為l~r內不同的數,若l~mid的sum>k 則下標r在左子樹中 否則k-=sum 下標在右子樹中 找到第一個不滿足的r即可 複雜度O(n*logn*logn)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=2e5+20; struct node{ int l,r,sum; //sum:字尾i中:下標l~r不同的數的個數 }T[N*40]; int x,y,n,m,a[N],last[N]; int cnt,root[N]; int build(int l,int r) { int rt=++cnt; T[rt].sum=0,T[rt].l=T[rt].r=0; if(l==r) return rt; int m=(l+r)>>1; T[rt].l=build(l,m); T[rt].r=build(m+1,r); return rt; } void update(int l,int r,int &x,int y,int v,int pos) { T[++cnt]=T[y],T[cnt].sum+=v,x=cnt; if(l==r) return; int m=(l+r)>>1; if(m>=pos) update(l,m,T[x].l,T[y].l,v,pos); else update(m+1,r,T[x].r,T[y].r,v,pos); } int query(int c,int l,int r,int k) { if(l==r) return l; int m=(l+r)>>1; int sum=T[T[c].l].sum; if(sum>k) return query(T[c].l,l,m,k); else return query(T[c].r,m+1,r,k-sum); } int main() { while(cin>>n) { cnt=0; memset(last,-1,sizeof(last)); for(int i=1;i<=n;i++) scanf("%d",&a[i]); build(1,n+1);//要返回第一個不滿足的r 所以n+1 //維護字尾i,l~r內不同的數的個數 for(int i=n;i>=1;i--) { int v=a[i]; if(last[v]==-1) update(1,n+1,root[i],root[i+1],1,i); else { update(1,n+1,root[i],root[i+1],-1,last[v]); update(1,n+1,root[i],root[i],1,i); } last[v]=i; } for(int k=1;k<=n;k++) { int L=1,ans=0; while(L<=n) { int pos=query(root[L],1,n+1,k); L=pos; ans++; } printf("%d ",ans); } printf("\n"); } return 0; }