• BZOJ3110 [Zjoi2013]K大数查询 树套树 线段树 整体二分 树状数组


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

    去博客园看该题解


    题目传送门 - BZOJ3110


    题意概括

      有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c。如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。

    N,M<=50000
    a<=b<=N
    1操作中abs(c)<=N
    2操作中c<=Maxlongint


     UPD(2018-04-01):之前抄的树套树是真的丑到爆。今天用分治做了一遍QAQ。

    题解

      让我们来考虑神奇的分治算法。

      整体二分!!(当你会了)

      首先当你已经掌握了树状数组的区间加和区间询问(如果不会->点这里

      我们考虑二分答案。

      注意进行以下操作要严格按照输入时间先后顺序来。

      首先对于加进去的数字c,我们把他变成n-c+1,这样就把询问前k大变成了前k小。

      如果是修改操作,如果修改的值比当前的mid值小,就修改,并扔到左区间里面。否则扔到右边。

      如果是询问操作,如果在当前的状态下,该询问的区间内查询到的数的个数res比当前询问的c要大(或者相等),那么显然答案在左区间,把他扔到左边,否则把他的c减掉res再扔到右边去。

      然后递归分治两个区间就可以了。

      (本质是个二分答案的升级版)

      

      然而博主非常非常非常非常非常非常的菜。千辛万苦调出样例,交一发WA。找了半天发现树状数组打萎掉了。

      然后推式子不下于3遍。校对lych大佬的代码不下于5遍,还是没发现错误。

      woc心态爆炸bonebonebone!

      还是没发现错误。

      

      

       

     

      发现了。最难发现的地方。tree[2][N]打成了tree[N][2]……QAQ

      

      

      

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=50005;
    int n,m,id[N],tmpL[N],tmpR[N];
    LL tree[2][N];
    int lowbit(int x){
    	return x&-x;
    }
    void add(int t,int x,int y){
    	for (;x<=n+1;x+=lowbit(x))
    		tree[t][x]+=y;
    }
    void update(int L,int R,int v){
    	add(0,L,v),add(1,L,v*L);
    	add(0,R+1,-v),add(1,R+1,-v*(R+1));
    }
    LL sum(int t,int x){
    	LL ans=0;
    	for (;x>0;x-=lowbit(x))
    		ans+=tree[t][x];
    	return ans;
    }
    LL query(int L,int R){
    	return sum(0,R)*(R+1)-sum(0,L)*L-sum(1,R)+sum(1,L);
    }
    struct opts{
    	int type,a,b,c,ans;
    	void get(){
    		scanf("%d%d%d%d",&type,&a,&b,&c);
    		if (type==1)
    			c=n-c+1;
    	}
    }a[N];
    void solve(int xL,int xR,int L,int R){
    	if (L>R)
    		return;
    	if (xL==xR){
    		for (int i=L;i<=R;i++)
    			a[id[i]].ans=xL;
    		return;
    	}
    	int xmid=(xL+xR)>>1;
    	int l=0,r=0;
    	for (int i=L;i<=R;i++)
    		if (a[id[i]].type==1){
    			if (a[id[i]].c<=xmid)
    				tmpL[++l]=id[i],update(a[id[i]].a,a[id[i]].b,1);
    			else
    				tmpR[++r]=id[i];
    		}
    		else {
    			LL res=query(a[id[i]].a,a[id[i]].b);
    			if (res>=a[id[i]].c)
    				tmpL[++l]=id[i];
    			else
    				tmpR[++r]=id[i],a[id[i]].c-=res;
    		}
    	for (int i=1;i<=l;i++)
    		if (a[tmpL[i]].type==1)
    			update(a[tmpL[i]].a,a[tmpL[i]].b,-1);
    	for (int i=L;i<=L+l-1;i++)
    		id[i]=tmpL[i-(L-1)];
    	for (int i=R-r+1;i<=R;i++)
    		id[i]=tmpR[i-(R-r)];
    	solve(xL,xmid,L,L+l-1);
    	solve(xmid+1,xR,R-r+1,R);
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=m;i++)
    		a[i].get(),id[i]=i;
    	memset(tree,0,sizeof tree);
    	solve(1,2*n+1,1,m);
    	for (int i=1;i<=m;i++)
    		if (a[i].type==2)
    			printf("%d
    ",n-a[i].ans+1);
    	return 0;
    }
    

      

    ———————old———————(2017-12-19)

    题解

      树套树裸题。

      外层套权值线段树,内层套区间线段树。

      标记永久化比较好写。

      空间随便卡卡就过去了。


    代码

    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <cstdlib>
    #include <cmath>
    using namespace std;
    typedef long long LL;
    const int N=50005,NN=N*2,K=220;
    struct Position_Segment_Tree{
    	int tot,ls[N*K],rs[N*K];
    	LL add[N*K],sum[N*K];
    	void clear(){
    		tot=0;
    		memset(ls,0,sizeof ls);
    		memset(rs,0,sizeof rs);
    		memset(add,0,sizeof add);
    		memset(sum,0,sizeof sum);
    	}
    	void update(int &rt,int le,int ri,int xle,int xri){
    		if (!rt)
    			rt=++tot;
    		if (xle<=le&&ri<=xri){
    			add[rt]++;
    			return;
    		}
    		sum[rt]+=xri-xle+1;
    		int mid=(le+ri)>>1;
    		if (xri<=mid)
    			update(ls[rt],le,mid,xle,xri);
    		else if (xle>mid)
    			update(rs[rt],mid+1,ri,xle,xri);
    		else {
    			update(ls[rt],le,mid,xle,mid);
    			update(rs[rt],mid+1,ri,mid+1,xri);
    		}
    	}
    	LL query(int rt,int le,int ri,int xle,int xri){
    		if (!rt)
    			return 0;
    		if (xle<=le&&ri<=xri)
    			return sum[rt]+add[rt]*(xri-xle+1);
    		int mid=(le+ri)>>1;
    		LL res=add[rt]*(xri-xle+1);
    		if (xri<=mid)
    			return res+query(ls[rt],le,mid,xle,xri);
    		else if (xle>mid)
    			return res+query(rs[rt],mid+1,ri,xle,xri);
    		else
    			return res+query(ls[rt],le,mid,xle,mid)
    					  +query(rs[rt],mid+1,ri,mid+1,xri);
    	}
    }PST;
    int n,nn,m;
    int tr[NN*4];
    void update(int rt,int le,int ri,int pos,int xle,int xri){
    	PST.update(tr[rt],1,n,xle,xri);
    	if (le==ri)
    		return;
    	int mid=(le+ri)>>1,ls=rt<<1,rs=ls|1;
    	if (pos<=mid)
    		update(ls,le,mid,pos,xle,xri);
    	else
    		update(rs,mid+1,ri,pos,xle,xri);
    }
    int query(int rt,int le,int ri,int xle,int xri,LL k){
    	if (le==ri)
    		return le;
    	int mid=(le+ri)>>1,ls=rt<<1,rs=ls|1;
    	LL Rz=PST.query(tr[rs],1,n,xle,xri);
    	if (k<=Rz)
    		return query(rs,mid+1,ri,xle,xri,k);
    	else
    		return query(ls,le,mid,xle,xri,k-Rz);
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	nn=n*2+1;
    	PST.clear();
    	while (m--){
    		int op,a,b,c;
    		scanf("%d%d%d%d",&op,&a,&b,&c);
    		if (op==1)
    			update(1,1,nn,c+n+1,a,b);
    		else
    			printf("%d
    ",query(1,1,nn,a,b,c)-n-1);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    机器学习手稿--NumPy篇
    机器学习手稿--PyTorch篇
    算法相关库
    如何保存用户的登录状态
    Go开发新手需知:Printf、Sprintf、Println 的区别
    二、Bean生命周期中AOP的流程
    Spring 注解驱动开发-IOC (精华版)
    我是如何编写流程图程序的?
    微前端架构设计之 WebSocket API 断连后重连的设计方案
    工具-使用distinct方法去重对象List
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ3110.html
Copyright © 2020-2023  润新知