• BZOJ1901 Zju2112 Dynamic Rankings 主席树


    欢迎访问~原文出处——博客园-zhouzhendong

    去博客园看该题解


    题目传送门 - BZOJ1901


    题意概括

      给你一段序列(n个数),让你支持一些操作(共m次),

      有两种操作,一种是询问区间第k小,一种是单点修改。

      n,m<=10000


    题解

      这个主席树的写法是我自己造出来的。

      主席树的查询区间第k大需要依赖前缀和。

      树状数组最擅长这个了,就让他来干。

      原理是这样的:

      先离散化,包括修改操作里面的数字也要离散化。

      然后建树,包括修改操作所涉及的数值也要建。

      现在总共有n个线段树。

      第0棵树是最完整的,故我们把这棵树的每一个节点都当作一个树状数组的起点,即该树状数组的第一项。

      然后往下一棵树走,当然下一棵树不一定有这个节点,那么不停的走下去,这个可以在建树的时候预先处理一个Next数组来优化。

      走到下一个这个位置的节点之后,我们再把这个节点作为该树状数组的第二项。

      这样我们有大约n*4(第0棵树的节点个数)个树状数组了。

      然而不处理要超空间。

      所以有两种方案,一种是vector,一种是把所有的树状数组连起来,变成一个大的。

      然而查询之类的就和普通的主席树差不多了。

    细节上要大大的注意!


    代码

    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <cstdlib>
    #include <cmath>
    #include <vector>
    using namespace std;
    const int N=10005,S=N*2*20*4;//v=4*node=4*20*hs=4*20*2*n
    struct Que{
    	char op;
    	int a,b,c;
    	void read(){
    		char opc[5];
    		scanf("%s%d%d",opc,&a,&b);
    		op=opc[0];
    		if (op=='Q')
    			scanf("%d",&c);
    	}
    }q[N];
    vector <int> vec[N];
    int n,m,v[N],Ha[N*2],hs=0,total=0,totarr=0;
    int root[N],ls[S],rs[S],sum[S],pos[S],Next[S],p[S];
    void LSH(){
    	int hs_=1;
    	sort(Ha+1,Ha+hs+1);
    	for (int i=2;i<=hs;i++)
    		if (Ha[i]!=Ha[i-1])
    			Ha[++hs_]=Ha[i];
    	hs=hs_;
    }
    int find(int x){
    	int L=1,R=hs,mid;
    	while (L<=R){
    		mid=(L+R)>>1;
    		if (Ha[mid]==x)
    			return mid;
    		if (Ha[mid]<x)
    			L=mid+1;
    		else
    			R=mid-1;
    	}
    	exit(233);
    }
    int build(int L,int R){
    	int rt=++total;
    	if (L==R){
    		ls[rt]=rs[rt]=0;
    		return rt;
    	}
    	int mid=(L+R)>>1;
    	ls[rt]=build(L,mid);
    	rs[rt]=build(mid+1,R);
    	return rt;
    }
    int lowbit(int x){
    	return x&-x;
    }
    void add(int x,int v){
    	for (;x<=totarr;x+=lowbit(x))
    		sum[x]+=v;
    }
    int Sum(int x){
    	int ans=0;
    	for (;x>0;x-=lowbit(x))
    		ans+=sum[x];
    	return ans;
    }
    void add_tree(int prt,int &rt,int L,int R,int pos){
    	if (!rt||rt==prt)
    		rt=++total;
    	Next[prt]=rt;
    	if (L==R)
    		return;
    	int mid=(L+R)>>1;
    	if (pos<=mid){
    		add_tree(ls[prt],ls[rt],L,mid,pos);
    		if (!rs[rt])
    			rs[rt]=rs[prt];
    	}
    	else {
    		add_tree(rs[prt],rs[rt],mid+1,R,pos);
    		if (!ls[rt])
    			ls[rt]=ls[prt];
    	}
    }
    void build_treearr(int srt,int L,int R){
    	for (int i=srt;i;i=Next[i])
    		p[i]=++totarr;
    	if (L==R)
    		return;
    	int mid=(L+R)>>1;
    	build_treearr(ls[srt],L,mid);
    	build_treearr(rs[srt],mid+1,R);
    }
    void update(int rt,int L,int R,int pos,int v){
    	add(p[rt],v);
    	if (L==R)
    		return;
    	int mid=(L+R)>>1;
    	if (pos<=mid)
    		update(ls[rt],L,mid,pos,v);
    	else
    		update(rs[rt],mid+1,R,pos,v);
    }
    int query(int prt,int rt,int L,int R,int k){
    	if (L==R)
    		return Ha[L];
    	int mid=(L+R)>>1;
    	int LV=Sum(p[ls[rt]])-Sum(p[ls[prt]]);
    	if (k<=LV)
    		return query(ls[prt],ls[rt],L,mid,k);
    	else
    		return query(rs[prt],rs[rt],mid+1,R,k-LV);
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++)
    		scanf("%d",&v[i]),Ha[++hs]=v[i];
    	for (int i=1;i<=m;i++){
    		q[i].read();
    		if (q[i].op=='C')
    			Ha[++hs]=q[i].b;
    	}
    	LSH();
    	for (int i=1;i<=n;i++)
    		vec[i].clear();
    	for (int i=1;i<=n;i++)
    		vec[i].push_back(v[i]);
    	for (int i=1;i<=m;i++)
    		if (q[i].op=='C')
    			vec[q[i].a].push_back(q[i].b);
    	memset(sum,0,sizeof sum);
    	memset(pos,0,sizeof pos);
    	memset(Next,0,sizeof Next);
    	memset(root,0,sizeof root);
    	root[0]=build(1,hs);
    	for (int i=1;i<=n;i++)
    		for (int j=0;j<vec[i].size();j++)
    			add_tree(root[i-1],root[i],1,hs,find(vec[i][j]));
    	build_treearr(root[0],1,hs);
    	for (int i=1;i<=n;i++)
    		update(root[i],1,hs,find(v[i]),1);
    	for (int i=1;i<=m;i++)
    		if (q[i].op=='C'){
    			update(root[q[i].a],1,hs,find(v[q[i].a]),-1);
    			update(root[q[i].a],1,hs,find(v[q[i].a]=q[i].b),1);
    		}
    		else
    			printf("%d
    ",query(root[q[i].a-1],root[q[i].b],1,hs,q[i].c));
    	return 0;
    } 
    

      

  • 相关阅读:
    MySQL面试题-基础
    IDEA 创建Spring项目后org.springframework.boot报错
    IDEA上传项目到SVN
    解决:idea中右键项目找不到subversion
    关于fromdata的上传文件问题
    PHP清除数组中为0的元素
    PHP删除数组中重复的元素
    PHP代码篇(六)--如何根据邀请人id查询满足条件的会员上级
    PHP对URL进行字符串编码
    PHP时间戳相互转换
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ1901.html
Copyright © 2020-2023  润新知