学会了CDQ之后,整体二分变得简单了起来。
对于一些需要多次二分的题目,并且二分的做法相同,我们可以考虑整体二分。就是说考虑分治,普通的二分做法我们一个一个查询,而这个把所有询问打包,一波查询,分配到两边递归下去。思想很好理解。
3110: [Zjoi2013]K大数查询
Time Limit: 20 Sec
Memory Limit: 512 MB
Description
有
N
N
个位置,个操作。操作有两种,每次操作如果是1
a
a
c
c
的形式表示在第个位置到第
b
b
个位置,每个位置加入一个数
如果是2
a
a
c
c
形式,表示询问从第个位置到第
b
b
个位置,第大的数是多少。
Input
第一行
N
N
,
接下来
M
M
行,每行形如1
b
b
或2
a
a
c
c
Output
输出每个询问的结果
Sample Input
2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
Sample Output
1
2
1
【样例说明】
第一个操作 后位置 1 的数只有 1 , 位置 2 的数也只有 1 。 第二个操作 后位置 1的数有 1 、 2 ,位置 2 的数也有 1 、 2 。 第三次询问 位置 1 到位置 1 第 2 大的数 是1 。 第四次询问 位置 1 到位置 1 第 1 大的数是 2 。 第五次询问 位置 1 到位置 2 第 3大的数是 1 。
a≤b≤N a ≤ b ≤ N
1操作中 abs(c)≤N a b s ( c ) ≤ N
2操作中
c≤Maxlongint
c
≤
M
a
x
l
o
n
g
i
n
t
解:
还是按照时间分治(只分治修改操作)。开一个线段树,支持区间修改区间查询。还是只做前一半修改,看查询够不够,够的分到前一半分治,不够就减去当前查询值分到后一半分治。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
bool key;
struct lxy{
int l,r,tim;
long long c,ans;
bool operator < (const lxy &QAQ)const{
if(key==1) return c<QAQ.c;
if(key==0) return tim<QAQ.tim;
}
}q[50005],m[500005],tax[500005];
struct segmentree{
int l,r,lson,rson,clr;
long long num,tag;
}b[100005];
int n,p,root,cntq,cntm,x,cnt;
void update(int u){b[u].num=b[b[u].lson].num+b[b[u].rson].num;}
void build(int &u,int l,int r)
{
u=++cnt;
b[u].l=l;b[u].r=r;
if(l==r) return;
int mid=(l+r)>>1;
build(b[u].lson,l,mid);
build(b[u].rson,mid+1,r);
}
void pushdown(int u)
{
if(b[u].clr!=0)
{
b[b[u].lson].num=0;b[b[u].lson].tag=0;
b[b[u].rson].num=0;b[b[u].rson].tag=0;
b[b[u].rson].clr=1;b[b[u].lson].clr=1;
b[u].clr=0;
}
if(b[u].tag!=0)
{
b[b[u].lson].num+=b[u].tag*(b[b[u].lson].r-b[b[u].lson].l+1);
b[b[u].rson].num+=b[u].tag*(b[b[u].rson].r-b[b[u].rson].l+1);
b[b[u].lson].tag+=b[u].tag;
b[b[u].rson].tag+=b[u].tag;
b[u].tag=0;
}
return;
}
long long query(int u,int l,int r)
{
pushdown(u);
if(b[u].l==l&&b[u].r==r) return b[u].num;
int mid=(b[u].l+b[u].r)>>1;
if(l>mid) return query(b[u].rson,l,r);
else if(r<=mid) return query(b[u].lson,l,r);
return query(b[u].lson,l,mid)+query(b[u].rson,mid+1,r);
}
void modify(int u,int l,int r)
{
pushdown(u);
if(b[u].l==l&&b[u].r==r)
{
b[u].num+=b[u].r-b[u].l+1;
b[u].tag++;
return;
}
int mid=(b[u].l+b[u].r)>>1;
if(l>mid) modify(b[u].rson,l,r);
else if(r<=mid) modify(b[u].lson,l,r);
else modify(b[u].lson,l,mid),modify(b[u].rson,mid+1,r);
update(u);
}
void cdq(int ml,int mr,int ql,int qr)
{
if(ml==mr){
for(int i=ql;i<=qr;i++)
q[i].ans=m[ml].c;
return;
}
b[root].clr=1;
b[root].tag=0;
b[root].num=0;
int mid=(ml+mr)>>1;
key=1,sort(m+ml,m+mr+1);
key=0,sort(m+mid+1,m+mr+1);
key=0,sort(q+ql,q+qr+1);
int t1=mid+1,t2=ql,l=ql-1,r=qr+1;
while(t2<=qr)
{
if(t1<=mr&&m[t1].tim<q[t2].tim)
modify(root,m[t1].l,m[t1].r),t1++;
else
{
long long y=query(root,q[t2].l,q[t2].r);
if(y>=q[t2].c) tax[--r]=q[t2];
else q[t2].c-=y,tax[++l]=q[t2];
t2++;
}
}
for(int i=ql;i<=qr;i++) q[i]=tax[i];
if(l>=ql) cdq(ml,mid,ql,l);
if(r<=qr) cdq(mid+1,mr,r,qr);
}
int main()
{
scanf("%d%d",&n,&p);
for(int i=1;i<=p;i++)
{
scanf("%d",&x);
if(x==2) cntq++,scanf("%d%d%lld",&q[cntq].l,&q[cntq].r,&q[cntq].c),q[cntq].tim=i;
if(x==1) cntm++,scanf("%d%d%lld",&m[cntm].l,&m[cntm].r,&m[cntm].c),m[cntm].tim=i;
}
build(root,1,n);
cdq(1,cntm,1,cntq);
key=0,sort(q+1,q+cntq+1);
for(int i=1;i<=cntq;i++)
printf("%lld\n",q[i].ans);
}