• Till I Collapse CodeForces


    对于每个数字(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;
    }
    
    
  • 相关阅读:
    分区表的一些操作例子
    MySQL 主从复制
    使用pipeline的函数
    主键字段使用不同数据类型的简单比较
    Flashback Query笔记
    基于Liquibase的数据库持续集成
    MySQL安装
    格式化SYS_GUID()成为标准格式
    Silverlight Treeview 相关操作:加载,保存,索引节点,节点移动,模板节点
    Silverlight TreeView组件的研究[2]
  • 原文地址:https://www.cnblogs.com/QingyuYYYYY/p/15057574.html
Copyright © 2020-2023  润新知