树套树做法:
注意到权值很小,于是外层开权值线段树,内层是一个动态开点的区间线段树,维护权值在[L,R],位置在[l,r]的数一共有多少个。
修改就是内层的一个线段树上区间加一。
查询时,外层线段树中先判断左子树中够不够k个数,若够则往左子树递归,不够就减一下后往右子树。
代码(MLE):
#include<iostream>
#include<cstdio>
using namespace std;
int n,m;
struct tree1
{
int sum,lz;
tree1 *ls,*rs;
tree1()
{
sum=lz=0;
ls=rs=NULL;
}
void cal(int x,int l,int r){sum+=x*(r-l+1);}
void pushdown(int l,int r)
{
int mid=(l+r)>>1;
if(ls==NULL) ls=new tree1;
if(rs==NULL) rs=new tree1;
ls->cal(lz,l,mid);rs->cal(lz,mid+1,r);
ls->lz+=lz;rs->lz+=lz;
lz=0;
}
void update()
{
sum=ls->sum+rs->sum;
}
void add(int lx,int rx,int l,int r)
{
if(lx==l&&rx==r) {lz++;sum+=r-l+1;return;}
int mid=(l+r)>>1;
pushdown(l,r);
if(rx<=mid) ls->add(lx,rx,l,mid);
else if(lx>mid) rs->add(lx,rx,mid+1,r);
else ls->add(lx,mid,l,mid),rs->add(mid+1,rx,mid+1,r);
update();
}
int qry(int lx,int rx,int l,int r)
{
if(lx==l&&rx==r) return sum;
pushdown(l,r);
int mid=(l+r)>>1;
if(rx<=mid) return ls->qry(lx,rx,l,mid);
else if(lx>mid) return rs->qry(lx,rx,mid+1,r);
else return ls->qry(lx,mid,l,mid)+rs->qry(mid+1,rx,mid+1,r);
}
};
struct tree2
{
int l,r;
tree2 *ls,*rs;
tree1 *rt;
tree2()
{
l=r=0;
ls=rs=NULL;rt=NULL;
}
void build(int lx,int rx)
{
l=lx;r=rx;
//(rt=new tree1)->init(1,n);
rt=new tree1;
//cout<
if(l==r) return;
int mid=(l+r)>>1;
(ls=new tree2)->build(lx,mid);
(rs=new tree2)->build(mid+1,rx);
}
void mdf(int lc,int rc,int c)
{
rt->add(lc,rc,1,n);
if(l==r) return;
int mid=(l+r)>>1;
if(c<=mid) ls->mdf(lc,rc,c);
else rs->mdf(lc,rc,c);
}
int query(int lc,int rc,int k)
{
if(l==r) return l;
int tmp=rs->rt->qry(lc,rc,1,n);
if(tmp<k) return ls->query(lc,rc,k-tmp);
else return rs->query(lc,rc,k);
}
}*xtr;
int main()
{
scanf("%d%d",&n,&m);
(xtr=new tree2)->build(0,n);
while(m--)
{
int opt,a,b,c;
scanf("%d%d%d%d",&opt,&a,&b,&c);
if(opt==1) xtr->mdf(a,b,c);
else printf("%d\n",xtr->query(a,b,c));
}
return 0;
}
CDQ分治做法:
solve(S,l,r)表示操作集合是S,且其中所有修改操作的权值都在[l,r]内,询问的答案也在[l,r]内,且所有操作是按时间顺序。
令mid=(l+r)/2,我们可以先做[l,mid]中的修改操作,就可以判断询问答案是在[l,mid]还是在[mid+1,r],然后递归即可。
用树状数组的奇技淫巧维护这个区间加和区间查询,最后记得减回去清空。
代码(AC):
#include
#include
#include
#include
#define ll long long
using namespace std;
const int maxn=50010;
int n,m,ans[maxn];
ll c[2][maxn];
bool b[maxn];
struct node
{
int o,x,y,z,id;
}q[maxn],nq[maxn];
bool cmp(node a,node b)
{
return a.idint k,int x,int d)
{
for(;x<=n;x+=(x&-x)) c[k][x]+=d;
}
ll qry(int k,int x)
{
ll r=0;
for(;x;x-=(x&-x)) r+=c[k][x];
return r;
}
void mdf(int l,int r,int f)
{
r++;
add(0,l,f);add(0,r,-f);add(1,l,f*l);add(1,r,-f*r);
}
ll qsum(int l,int r)
{
l--;
return qry(0,r)*(r+1)-qry(1,r)-qry(0,l)*(l+1)+qry(1,l);
}
void solve(int l,int r,int lx,int rx)
{
if(lx>rx) return ;
if(l==r)
{
for(int i=lx;i<=rx;i++)
if(q[i].o) ans[q[i].id]=l;
return ;
}
int mid=(l+r)>>1;
for(int i=lx;i<=rx;i++) b[i]=0;
for(int i=lx;i<=rx;i++)
if(q[i].o)
{
int tmp=qsum(q[i].x,q[i].y);
if(tmp<q[i].z) {b[i]=1;q[i].z-=tmp;}
}
else if(q[i].z<=mid) mdf(q[i].x,q[i].y,1); else b[i]=1;
for(int i=lx;i<=rx;i++)
if(!q[i].o&&q[i].z<=mid) mdf(q[i].x,q[i].y,-1);
int top=lx-1,smid;
for(int i=lx;i<=rx;i++)
if(!b[i]) nq[++top]=q[i];
smid=top;
for(int i=lx;i<=rx;i++)
if(b[i]) nq[++top]=q[i];
for(int i=lx;i<=rx;i++)
q[i]=nq[i];
solve(l,mid,lx,smid);
solve(mid+1,r,smid+1,rx);
}
int main()
{
scanf("%d%d",&n,&m);
int cnt=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d%d",&q[i].o,&q[i].x,&q[i].y,&q[i].z);
q[i].o--;q[i].id=i;
if(q[i].o) q[i].id=++cnt;
else q[i].z=n-q[i].z+1;
}
solve(1,n,1,m);
for(int i=1;i<=cnt;i++)
printf("%d\n",n-ans[i]+1);
return 0;
}