• bzoj 3110 [Zjoi2013]K大数查询【树套树||整体二分】


    树套树:

    约等于是个暴力了。以区间线段树的方式开一棵权值线段树,在权值线段树的每一个点上以动态开点的方式开一棵区间线段树。
    结果非常惨烈(时限20s)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N=23000005,M=500005;
    int n,m,tot,rt[M],sz,lz[N];
    long long sum[N],mp[M];
    struct  data
    {
    	int l,r;
    }tr[N];
    struct node
    {
    	int a,b,op;
    	long long c;
    }ans[M];
    int find(long long x)
    {
    	return lower_bound(mp+1,mp+tot+1,x)-mp-1;
    }
    void pushdown(int x,int l,int r)
    {
    	int mid=(l+r)/2;
    	if(!tr[x].l)
    		tr[x].l=++sz;
    	if(!tr[x].r)
    		tr[x].r=++sz;
    	int a=tr[x].l,b=tr[x].r;
    	lz[a]+=lz[x];
    	lz[b]+=lz[x];
    	sum[a]+=(long long)(mid-l+1)*(long long)lz[x];
    	sum[b]+=(long long)(r-mid)*(long long)lz[x];
    	lz[x]=0;
    }
    void change(int &ro,int l,int r,int ll,int rr)
    {
    	if(!ro)
    		ro=++sz;
    	if(lz[ro])
    		pushdown(ro,l,r);
    	if(l>=ll&&r<=rr)
    	{
    		sum[ro]+=(long long)(r-l+1);
    		lz[ro]++;
    		return;
    	}
     	int mid=(l+r)/2;
    	if(ll<=mid)
    		change(tr[ro].l,l,mid,ll,rr);
    	if(rr>mid)
    		change(tr[ro].r,mid+1,r,ll,rr);
    	sum[ro]=(long long)sum[tr[ro].l]+sum[tr[ro].r];
    }
    void update(int a,int b,int c,int now,int l,int r)
    {
    	change(rt[now],1,n,a,b);
    	if(l==r)
    		return;
    	int mid=(l+r)/2;
    	if(c<=mid)
    		update(a,b,c,now<<1,l,mid);
    	else
    		update(a,b,c,now<<1|1,mid+1,r);
    }
    long long query(int k,int l,int r,int ll,int rr)
    {
    	if(lz[k])
    		pushdown(k,l,r);
    	if (l>=ll&&r<=rr)
    		return sum[k];
    	int mid=(l+r)/2;
    	long long ans=0;
    	if(ll<=mid)
    		ans+=query(tr[k].l,l,mid,ll,rr);
    	if(rr>mid)
    		ans+=query(tr[k].r,mid+1,r,ll,rr);
    	return ans;
    }
    int ques(int a,int b,long long c)
    {
    	int l=1,r=tot,k=1;
    	while(l!=r)
    	{
    		int mid=(l+r)>>1;
    		long long t=query(rt[k<<1],1,n,a,b);
    		if(t>=c)
    			r=mid,k<<=1;
    		else
    			l=mid+1,k=k<<1|1,c-=(long long)t;
    	}
    	return l;
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=m;i++)
    	{
    		scanf("%d%d%d%lld",&ans[i].op,&ans[i].a,&ans[i].b,&ans[i].c);
    		if(ans[i].op==1)
    			mp[++tot]=ans[i].c;
    	}
    	sort(mp+1,mp+tot+1);
    	tot=unique(mp+1,mp+tot+1)-mp-1;
    	for(int i=1;i<=m;i++)
    		if(ans[i].op==1)
    		{
    			int k=find(ans[i].c)+1;
    			update(ans[i].a,ans[i].b,k,1,1,tot);
    		}
    		else
    		{
    			long long cnt=query(rt[1],1,n,ans[i].a,ans[i].b);
    			printf("%lld
    ",mp[ques(ans[i].a,ans[i].b,cnt-ans[i].c+1)]);
    		}
    	return 0;
    }
    

    整体二分

    首先我一开始没有注意到的:
    1操作中abs(c)<=N
    2操作中c<=Maxlongint
    也就是说答案在0到n之间,那么久可以整体二分来做了
    维护一棵区间线段树,维护区间加标记和区间清零标记。
    每次操作前清零
    设当前答案区间为[l,r],询问区间为[x,y],mid=(l+r)>>1;
    对于插入操作:如果插入的数>mid,就在其区间上+1,放到前一堆(我不知道怎么形容了,否则不操作,放倒后一堆;
    对于查询操作:如果查询区间内的数符合k个要求,放到前一堆,否则放到后一堆。
    直到l==r,更新[x,y]内的答案
    写丑了

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int N=500005;
    int n,m,ans[N];
    long long sum[N*4],lz[N*4],tg[N*4];
    struct qwe
    {
    	int l,r,id,n,p,x;
    }a[N];
    int read()
    {
        int r=0,f=1;
        char p=getchar();
        while(p>'9'||p<'0')
        {
            if(p=='-')
                f=-1;
            p=getchar();
        }
        while(p>='0'&&p<='9')
        {
            r=r*10+p-48;
            p=getchar();
        }
        return r*f;
    }
    void clear(int now)
    {
    	sum[now]=lz[now]=0;
    	tg[now]=1;
    }
    bool cmp(const qwe &a,const qwe &b)
    {
    	return a.n<b.n;
    }
    void pd(int now,int l,int r)
    {
    	if(tg[now])
    	{
    		clear(now<<1); 
    		clear(now<<1|1);
    		tg[now]=0;
    	}
    	int mid=(l+r)/2;
    	if(lz[now])
    	{
    		lz[now<<1]+=lz[now];
    		lz[now<<1|1]+=lz[now];
    		sum[now<<1]+=lz[now]*(mid-l+1);
    		sum[now<<1|1]+=lz[now]*(r-mid);
    		lz[now]=0;
    	}
    }
    void ud(int now)
    {
    	sum[now]=sum[now<<1]+sum[now<<1|1];
    }
    void update(int now,int l,int r,int ll,int rr)
    {
    	if(ll<=l&&r<=rr)
    	{
    		sum[now]+=(r-l+1);
    	 	lz[now]++;
    	 	return;
    	}
    	int mid=(l+r)/2;
    	pd(now,l,r);
    	if(ll<=mid) 
    		update(now<<1,l,mid,ll,rr);
    	if(rr>mid) 
    		update(now<<1|1,mid+1,r,ll,rr);
    	ud(now);
    }
    long long ques(int now,int l,int r,int ll,int rr)
    {
    	if(ll<=l&&r<=rr)
    		return sum[now];
    	int mid=(l+r)/2;
    	pd(now,l,r);
    	long long ans=0;
    	if(ll<=mid) 
    		ans+=ques(now<<1,l,mid,ll,rr);
    	if(rr>mid) 
    		ans+=ques(now<<1|1,mid+1,r,ll,rr);
    	return ans;
    }
    void erfen(int l,int r,int x,int y)
    {
    	if(l==r)
    	{
    	 	for(int i=x;i<=y;i++)
    			if(a[i].p==2)
    				ans[a[i].id]=l;
    	 	return;
    	}
    	int mid=(l+r)/2,pl=0,pr=y-x+1;
    	clear(1);
    	for(int i=x;i<=y;i++) 
    		if(a[i].p==1)
    		{
    			if(a[i].x<=mid)  
    				a[i].n=++pl;
    			else
    			{
    				update(1,1,n,a[i].l,a[i].r);
    				a[i].n=++pr;
    			} 
    		}
    		else
    		{
    			long long t=ques(1,1,n,a[i].l,a[i].r);
    			if(t>=a[i].x)
    				a[i].n=++pr;
    			else
    			{
    				a[i].x=a[i].x-t;
    				a[i].n=++pl;
    			}
    		}
    	sort(a+x,a+y+1,cmp);
    	erfen(l,mid,x,x+pl-1);
    	erfen(mid+1,r,x+pl,y);
    }
    int main()
    {
    	n=read(),m=read();
    	for(int i=1;i<=m;i++)
    		a[i].p=read(),a[i].l=read(),a[i].r=read(),a[i].x=read(),a[i].id=i;
    	erfen(0,n,1,m);
    	for(int i=1;i<=m;i++)  
    		if(ans[i]) 
    			printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    kibana简单使用
    全文检索 高亮显示 数据库同步添加
    算法: Reverse Bits 反转位收藏
    算法:Number of 1 Bits 收藏
    JS创建二维数组
    查询状态在1,2,5,7的记录-oracle
    oracle 修改某字段,判断是否为空,如果为空赋默认值
    在运筹学中什么样的解决方案是最优的
    项目开发失败
    筛选符合条件的数据后,如何做数据源,绑定repeater
  • 原文地址:https://www.cnblogs.com/lokiii/p/8127436.html
Copyright © 2020-2023  润新知