对于每个数字(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;
}