BZOJ 3110 [Zjoi2013]K大数查询
2016-03-08 14:56
417 查看
title: ‘BZOJ 3110 [Zjoi2013]K大数查询’
categories: BZOJ
date: 2016-2-3 00:00:00
tags: [树套树,整体二分]
如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
接下来M行,每行形如1 a b c或2 a b c
2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
output.txt
1
2
1
首先这题一个位置可以加多个数,一开始没有看懂题意很坑爹。。。
这样一来,我们可以对开一个二维线段树,外层是权值线段树,内层是区间线段树。这样做的意义是,对每一个权值的区间都用一个线段树来维护其出现的位置和次数。因为树套树太耗内存了,还是在修改的时候新建节点比较好。。。
在外层那颗线段树写类似于二分的非递归就好了,因为并不需要用子节点来更新他的信息。
还有因为我太弱了所以并没有写标记永久化……感觉要去学习一下新姿势了。
感觉一般可以用树套树做的题目整体二分也是可以的?权值那层线段树只需要二分就好了,然后再用一个线段树来处理整体二分时候对询问信息的处理。
categories: BZOJ
date: 2016-2-3 00:00:00
tags: [树套树,整体二分]
Description
有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
Input
第一行N,M接下来M行,每行形如1 a b c或2 a b c
Output
输出每个询问的结果。Sample
input.txt2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
output.txt
1
2
1
Solution
好久没更博客了。。首先这题一个位置可以加多个数,一开始没有看懂题意很坑爹。。。
这样一来,我们可以对开一个二维线段树,外层是权值线段树,内层是区间线段树。这样做的意义是,对每一个权值的区间都用一个线段树来维护其出现的位置和次数。因为树套树太耗内存了,还是在修改的时候新建节点比较好。。。
在外层那颗线段树写类似于二分的非递归就好了,因为并不需要用子节点来更新他的信息。
还有因为我太弱了所以并没有写标记永久化……感觉要去学习一下新姿势了。
感觉一般可以用树套树做的题目整体二分也是可以的?权值那层线段树只需要二分就好了,然后再用一个线段树来处理整体二分时候对询问信息的处理。
Code 1:树套树
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define maxn (50000+5) #define maxm (5000000+5) using namespace std; struct Seg_Tree{ int lc,rc,sum,tag; }tr[maxm<<2]; int root[maxn<<2]; int n,m,cnt; inline int in(){ int x=0; char ch=getchar(); while(ch<'0' || ch>'9') ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return x; } void Update(int ind){ tr[ind].sum=tr[tr[ind].lc].sum+tr[tr[ind].rc].sum; } void Pushdown(int ind,int l,int r){ if(tr[ind].tag && l!=r){ if(!tr[ind].lc) tr[ind].lc=++cnt; if(!tr[ind].rc) tr[ind].rc=++cnt; int L=tr[ind].lc,R=tr[ind].rc,m=(l+r)>>1; tr[L].tag+=tr[ind].tag; tr[R].tag+=tr[ind].tag; tr[L].sum+=tr[ind].tag*(m-l+1); tr[R].sum+=tr[ind].tag*(r-m); } tr[ind].tag=0; } void Modify(int &ind,int l,int r,int ql,int qr){ if(!ind) ind=++cnt; Pushdown(ind,l,r); if(l==ql && r==qr){ tr[ind].sum+=(r-l+1); tr[ind].tag++; return; } int mid=(l+r)>>1; if(qr<=mid) Modify(tr[ind].lc,l,mid,ql,qr); else if(ql>mid) Modify(tr[ind].rc,mid+1,r,ql,qr); else Modify(tr[ind].lc,l,mid,ql,mid),Modify(tr[ind].rc,mid+1,r,mid+1,qr); Update(ind); } int Getsum(int ind,int l,int r,int ql,int qr){ if(!ind) return 0; Pushdown(ind,l,r); if(l==ql && r==qr) return tr[ind].sum; int mid=(l+r)>>1,tmp; if(qr<=mid) tmp=Getsum(tr[ind].lc,l,mid,ql,qr); else if(ql>mid) tmp=Getsum(tr[ind].rc,mid+1,r,ql,qr); else tmp=Getsum(tr[ind].lc,l,mid,ql,mid)+Getsum(tr[ind].rc,mid+1,r,mid+1,qr); Update(ind); return tmp; } void Insert(int a,int b,int c){ int l=1,r=n,ind=1; while(l<r){ Modify(root[ind],1,n,a,b); int mid=(l+r)>>1; if(c<=mid) r=mid,ind=ind<<1; else l=mid+1,ind=ind<<1|1; } Modify(root[ind],1,n,a,b); } int Query(int a,int b,int c){ int l=1,r=n,ind=1; while(l<r){ int mid=(l+r)>>1; int s=Getsum(root[ind<<1],1,n,a,b); if(s>=c) r=mid,ind=ind<<1; else l=mid+1,ind=ind<<1|1,c-=s; } return l; } int main(){ n=in(); m=in(); for(int i=1;i<=m;i++){ int opt,a,b,c; opt=in(); a=in(); b=in(); c=in(); if(opt==1) Insert(a,b,n-c+1); else printf("%d\n",n-Query(a,b,c)+1); } return 0; }
Code 2:整体二分
#include<cstdio> #include<climits> #include<cstdlib> #include<cstring> #include<algorithm> #define maxn 50000+5 using namespace std; struct Seg_Tree{ int l,r,mem; int tag,sum,sz; }tr[maxn<<4]; struct Query{ int ct,l,r,c,pos,k; }q[maxn]; int res[maxn]; int n,m; bool cmp(const Query &s,const Query &b){ return s.k<b.k; } void Update(int k){ tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum; } void Pushdown(int k){ int l=k<<1,r=k<<1|1; if(tr[k].mem){ tr[l].tag=tr[r].tag=tr[l].sum=tr[r].sum=0; tr[l].mem=tr[r].mem=1; tr[k].mem=0; } if(tr[k].tag){ tr[l].sum+=tr[k].tag*(tr[l].r-tr[l].l+1); tr[r].sum+=tr[k].tag*(tr[r].r-tr[r].l+1); tr[l].tag+=tr[k].tag; tr[r].tag+=tr[k].tag; tr[k].tag=0; } } void Build(int l,int r,int k){ tr[k].l=l; tr[k].r=r; if(l==r) return; int mid=(l+r)>>1; Build(l,mid,k<<1); Build(mid+1,r,k<<1|1); } void Add(int l,int r,int val,int k){ Pushdown(k); if(tr[k].l==l && tr[k].r==r){ tr[k].tag+=val; tr[k].sum+=val*(r-l+1); return; } if(r<=tr[k<<1].r) Add(l,r,val,k<<1); else if(l>=tr[k<<1|1].l) Add(l,r,val,k<<1|1); else Add(l,tr[k<<1].r,val,k<<1),Add(tr[k<<1|1].l,r,val,k<<1|1); Update(k); } int Getsum(int l,int r,int k){ Pushdown(k); if(tr[k].l==l && tr[k].r==r) return tr[k].sum; if(r<=tr[k<<1].r) return Getsum(l,r,k<<1); else if(l>=tr[k<<1|1].l) return Getsum(l,r,k<<1|1); else return Getsum(l,tr[k<<1].r,k<<1)+Getsum(tr[k<<1|1].l,r,k<<1|1); } void solve(int l,int r,int x,int y){ if(l==r){ for(int i=x;i<=y;i++) if(q[i].ct==2) res[q[i].pos]=l; return; } int mid=(l+r)>>1,pl=0,pr=y-x+1; tr[1].mem=1; tr[1].tag=tr[1].sz=tr[1].sum=0; for(int i=x;i<=y;i++) if(q[i].ct==1){ if(q[i].c<=mid) q[i].k=++pl; else{ Add(q[i].l,q[i].r,1,1); q[i].k=++pr; } } else{ int sum=Getsum(q[i].l,q[i].r,1); if(sum<q[i].c){ q[i].k=++pl; q[i].c-=sum; } else q[i].k=++pr; } sort(q+x,q+y+1,cmp); solve(l,mid,x,x+pl-1); solve(mid+1,r,x+pl,y); } int main(){ freopen("3110.in","r",stdin); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d%d%d",&q[i].ct,&q[i].l,&q[i].r,&q[i].c); q[i].pos=i; } Build(1,n,1); solve(0,n,1,m); for(int i=1;i<=m;i++) if(res[i]) printf("%d\n",res[i]); return 0; }
相关文章推荐
- jedis调用redis之String数据Key-Value
- 出租车GPS轨迹数据和手机数据的研究价值
- 离开微软这两年
- TextView 动态设置图片
- 动画入门
- SK-Learn使用NMF(非负矩阵分解)和LDA(隐含狄利克雷分布)进行话题抽取
- ubuntu下使用libsvm
- 当前时间
- Mysql INNER,LEFT ,RIGHT join的使用
- java发送http的get、post请求
- Android开发实现调用相册图片并裁剪上传功能
- js比较日期字符串
- Leetcode ☞ 203. Remove Linked List Elements ☆ 【dummy 方法二重点掌握】 【方法三双重指针】
- Last_Error: Slave SQL thread retried transaction 10 time(s) in vain, giving up. Consider raising the
- [小技巧] 如何自动将阿拉伯数字转换为中文大写数字
- android中跨进程通讯的4种方式
- media query ie8- 兼容实现总结
- Effiective C++ (一)
- #include "*.c"文件的妙用
- codeforces 651A Joysticks