Till I Collapse CodeForces - 786C 主席樹
阿新 • • 發佈:2021-07-25
對於每個數字\(a_i\) ,我們都建立一個新的版本,如果之前已經出現過,假設位置為\(last_{a_i}\)就在第 \(i\) 個版本的 \(last_{a_i}\) 刪去,然後在新的位置更新
這樣的話,對於每個k,我們從第n個版本開始,查詢子樹大小為 \(i+1\) ,且最左邊的的數字出現的版本,然後更新,不停的查詢然後更新答案
具體看程式碼
//主席樹 //難以處理區間修改操作,很難處理懶標記 //l,r代表左右子節點的下標 //cnt表示當前區間中一共多少個數 //離散化 //在數值上建立線段樹,維護每個數值區間中一共多少個數 // #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <vector> using namespace std; const int N = 200010; int n, m; int a[N]; int b[N]; vector<int> nums; int last[N]; struct Node { int l, r; int cnt; } tr[N << 5]; int root[N << 5], idx; //建樹,原始的,沒插入數字的樹 void build(int &p, int l, int r) { p = ++ idx; if (l == r) return ; //左右兒子 int mid = l + r >> 1; build(tr[p].l, l, mid), build(tr[p].r, mid + 1, r); } void del(int &p,int pre,int l,int r,int pos) { p=++idx; tr[p]=tr[pre]; if(l==r) { tr[p].cnt=0; return ; } int mid=l+r>>1; if(pos>mid) del(tr[p].r,tr[pre].r,mid+1,r,pos); else del(tr[p].l,tr[pre].l,l,mid,pos); tr[p].cnt=tr[tr[p].l].cnt + tr[tr[p].r].cnt; } void add(int &p,int pre,int l,int r,int pos) { p=++idx; tr[p]=tr[pre]; if(l==r) { tr[p].cnt=1; return ; } int mid=l+r>>1; if(pos>mid) add(tr[p].r,tr[pre].r,mid+1,r,pos); else add(tr[p].l,tr[pre].l,l,mid,pos); tr[p].cnt=tr[tr[p].l].cnt + tr[tr[p].r].cnt; } int find (int p, int l, int r, int k) { if (l == r && k != 1) return -1; else if (l == r) return l; int mid = l + r >> 1; if(tr[tr[p].r].cnt>=k) return find(tr[p].r, mid + 1, r, k); return find(tr[p].l, l, mid, k-tr[tr[p].r].cnt); } int main() { cin >> n; build(root[0], 1, n); for(int i=1; i<=n; i++) { int x; cin>>x; if(last[x]) del(root[i],root[i-1],1,n,last[x]); else root[i]=++idx,tr[root[i]]=tr[root[i-1]]; add(root[i],root[i],1,n,i); last[x]=i; } for(int i=1; i<=n; i++) { int ans=0,pr=n; while(pr>0) { if(tr[root[pr]].cnt<=i) { ans++; break; } pr=find(root[pr],1,n,i+1); ans++; } cout<<ans<<" "; } cout<<endl; return 0; }