zoukankan      html  css  js  c++  java
  • BZOJ 1901 洛谷 P2617 ZOJ 2112 Dynamic Rankings

    以下时空限制来自zoj Time limit 10000 ms Memory limit 32768 kB OS Linux Source Online Contest of Christopher's Adventure Author XIN, Tao

    吐槽

    zoj卡空间卡得太死了……32MB,远远不够放没优化过的主席树套树状数组……刚开始时段错误,还以为是t数组没开够,还是学得不扎实,别人说是就是,都没自己想想算算……后来二分骗数据,发现在还没开始操作、正在建最初的主席树时空间就爆了。没有优化的主席树,t数组要开到N*100左右,但这题最多给开到N*40……所以逼着我们优化空间。

    平台 n m 空间限制
    洛谷 1e5 1e5 1000MB
    BZOJ 1e4 1e4 128MB
    ZOJ 5e4 1e4 32MB

    解题思路

    1. 分块+二分查找 https://blog.csdn.net/qq_35320178/article/details/81077787
    2. 整体二分、cdq分治 https://www.cnblogs.com/ScratchingBear/p/5345820.html
    3. 线段树套平衡树、权值线段树套平衡树 https://blog.csdn.net/htt_h/article/details/47701417
    4. 树状数组套主席树 https://www.cnblogs.com/Empress/p/4659824.html
      • 这篇博客讲得是真的好,垃圾百度把它排到贼靠后

    源代码

    这个是我在BZOJ(本机测的darkbzoj数据,把N和M改小即可)、洛谷上的AC代码,主要参考洛谷zsy的题解。ZOJ的留坑,打算把优化空间的方法、整体二分、cdq分治学了。

    // luogu-judger-enable-o2
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    const int N=100010,M=100010;
    int T;
    int n,m;
    int a[N];
    struct Opt{
    	char mode;
    	int l,r,k;
    }opt[M];
    
    int val[N+M],sz;
    int id(int x){return std::lower_bound(val+1,val+sz+1,x)-val;}
    
    struct Node{
    	int lson,rson;
    	long long sum;
    }t[N*400];
    int root[N],cnt=0;
    
    int inline lowbit(int x){return x&-x;}
    
    void update_tree(int &x,int l,int r,int pos,int k)
    {//pre好像不需要啊,第x个节点的值并不用从pre继承过来,因为套上树状数组以后就不是前缀和了。参考zsy博客
    	if(!x) x=cnt++;//x等于0说明还没分配过
    	t[x].sum+=k;
    	if(l==r) return;
    	int mid=l+r>>1;
    	if(pos<=mid) update_tree(t[x].lson,l,mid,pos,k);
    	else update_tree(t[x].rson,mid+1,r,pos,k);
    }
    void change(int pos,int k)//数字a[pos]数量加k(先把之前的值去掉)
    {
    	int value=id(a[pos]);
    	for(int i=pos;i<=n;i+=lowbit(i))
    		update_tree(root[i],1,sz,value,k);
    }
    int temp[2][20];
    //记录询问时需要统计的节点编号,因为根有root数组,可以root[x-lowbit(x)],但下来的儿子孙子节点不能,所以先把需要统计的根记录在temp数组中,然后统计完一层以后将temp数组记录的节点向下挪动。
    //temp[0]记录第l-1棵线段树上的节点编号,temp[1]记录第r棵线段树上的节点编号。
    //由于节点已经存到temp中了,所以que_tree函数的参数就少了很多了。
    int que_tree(int l,int r,int k)
    {
    	if(l==r) return l;
    	int mid=l+r>>1,delta=0;
    	for(int i=1;i<=temp[1][0];i++) delta+=t[t[temp[1][i]].lson].sum;
    	for(int i=1;i<=temp[0][0];i++) delta-=t[t[temp[0][i]].lson].sum;//统计那几棵树的左儿子的sum,用来和k比较,找出从左还是从右
    	if(k<=delta)//向左走
    	{
    		for(int i=1;i<=temp[0][0];i++) temp[0][i]=t[temp[0][i]].lson;
    		for(int i=1;i<=temp[1][0];i++) temp[1][i]=t[temp[1][i]].lson;//temp也向下走
    		return que_tree(l,mid,k);
    	}
    	else
    	{
    		for(int i=1;i<=temp[0][0];i++) temp[0][i]=t[temp[0][i]].rson;
    		for(int i=1;i<=temp[1][0];i++) temp[1][i]=t[temp[1][i]].rson;
    		return que_tree(mid+1,r,k-delta);
    	}
    	
    }
    
    int que(int l,int r,int k)
    {
    	temp[0][0]=temp[1][0]=0;//用于统计temp[0]和temp[1]中存了多少个节点下标
    	for(int i=l-1;i;i-=lowbit(i))
    		temp[0][++temp[0][0]]=root[i];
    	for(int i=r;i;i-=lowbit(i))
    		temp[1][++temp[1][0]]=root[i];
    	return que_tree(1,sz,k);
    }
    /******************调试代码******************/
    //用于输出主席树上每棵线段树所代表的序列
    int quesum(int x,int l,int r,int pos)
    {
    	if(l==pos&&pos==r) return t[x].sum;
    	int mid=l+r>>1;
    	if(pos<=mid) return quesum(t[x].lson,l,mid,pos);
    	else return quesum(t[x].rson,mid+1,r,pos);
    }
    inline void debug()
    {
    	for (int i = 1; i <= sz; i++)
    		printf("%d ", val[i]);
    	puts("
    ****************");
    	for (int rt = 0; rt <= n; rt++, puts(""))
    		for (int i = 1; i <= sz; i++)
    			printf("%d ", quesum(root[rt], 1, sz, i));
    	puts("******************");
    }
    /*******************调试代码完*****************/
    int main()
    {
    	//freopen("test.in","r",stdin);
    	//scanf("%d",&T);
    	//while(T--)
    	{
    		sz=0;
    		scanf("%d%d",&n,&m);
    		for(int i=1;i<=n;i++) scanf("%d",a+i),val[++sz]=a[i];
    		for(int i=1;i<=m;i++)
    		{
    			char mode[2];
    			int l,r,k;
    			scanf("%s",mode);
    			if(mode[0]=='Q')
    			{
    				scanf("%d%d%d",&l,&r,&k);
    				opt[i]={mode[0],l,r,k};
    			}
    			else
    			{
    				scanf("%d%d",&l,&k);
    				opt[i]={mode[0],l,0,k};
    				val[++sz]=k;
    			}
    		}
    
    		std::sort(val+1,val+1+sz);
    		sz=std::unique(val+1,val+sz+1)-val-1;
    		
    		//memset(root,0,sizeof(root));
    		//memset(t,0,sizeof(t));
    		cnt=1;
    		for(int i=1;i<=n;i++)
    			change(i,1);
    		//debug();
    		for(int i=1;i<=m;i++)
    		{
    			if(opt[i].mode=='Q')
    			{
    				printf("%d
    ",val[que(opt[i].l,opt[i].r,opt[i].k)]);
    			}
    			else
    			{
    				change(opt[i].l,-1);
    				a[opt[i].l] = opt[i].k;
    				change(opt[i].l,1);
    				//debug();
    			}
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    WPF路由事件三:自定义路由事件
    SQL Server数据类型
    WPF路由事件二:路由事件的三种策略
    WPF路由事件一:逻辑树和可视树
    2015年09月12日
    PN结加正向偏置电压 其空间电荷区为何变窄
    解决win10 关键错误开始菜单和cortana无法工作 的问题(转-真的成功了)
    python的with语句,超级强大
    python安装图文教程---超详细。。。不过是转的,但有改动
    python try/except/finally
  • 原文地址:https://www.cnblogs.com/wawcac-blog/p/11266889.html
Copyright © 2011-2022 走看看