题意:链接
方法:树套树(线段树套线段树)
题解:这题好神啊- -自己在做的时候一顿yy也没yy出用两个线段树来搞,其实我想的是类似二逼那道题那样,用线段树维护总区间,treap维护每个节点,不过这样的话,尼玛修改就是暴力有没有?而且查询的时候也是暴力啊有没有?绝壁不是这么做的啊!
上网上找了找题解综合了大家的思想自己也是懂了这题是咋回事了,也是跪了。
好不扯淡了,谈正经的,两个线段树是怎么搞得。
其实第一棵线段树是区间的线段树,而第二棵是维护值域的线段树,具体解析请看代码。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 20000100
using namespace std ;
int lson[N],rson[N];
int sum[N],col[N];
int root[N];
int n,m,size;
int a,b,c;
void pushup(int rt)
{
sum[rt]=sum[lson[rt]]+sum[rson[rt]];
}
void pushdown(int rt,int l,int r)
{
if(!col[rt]||l==r)return;
if(!lson[rt])lson[rt]=++size;
if(!rson[rt])rson[rt]=++size;
col[lson[rt]]+=col[rt];
col[rson[rt]]+=col[rt];
int mid=(l+r)>>1;
sum[lson[rt]]+=(mid-l+1)*col[rt];
sum[rson[rt]]+=(r-mid)*col[rt];
col[rt]=0;
}
void update(int &rt,int l,int r,int L,int R)
{
if(!rt)rt=++size;
pushdown(rt,l,r);
if(L==l&&r==R)
{
sum[rt]+=r-l+1;
col[rt]++;
return;
}
int mid=(l+r)>>1;
if(R<=mid)update(lson[rt],l,mid,L,R);
else if(L>mid)update(rson[rt],mid+1,r,L,R);
else
{
update(lson[rt],l,mid,L,mid);
update(rson[rt],mid+1,r,mid+1,R);
}
pushup(rt);
}
int query(int rt,int l,int r,int L,int R)
{
if(!rt)return 0;
pushdown(rt,l,r);
if(L==l&&r==R)return sum[rt];
int mid=(l+r)>>1;
if(R<=mid)return query(lson[rt],l,mid,L,R);
else if(L>mid)return query(rson[rt],mid+1,r,L,R);
else return query(lson[rt],l,mid,L,mid)+query(rson[rt],mid+1,r,mid+1,R);
}
int work(int L,int R)
{
int l=1,r=n,rt=1;
while(l!=r)
{
int mid=(l+r)>>1;
int ans=query(root[rt<<1|1],1,n,L,R);
if(ans<c){c-=ans,r=mid,rt<<=1;}
else{l=mid+1,rt=rt<<1|1;}
}
return r;
}
void insert(int L,int R)
{
int l=1,r=n,rt=1;
while(l!=r)
{
update(root[rt],1,n,L,R);
int mid=(l+r)>>1;
if(c<=mid){r=mid,rt<<=1;}
else{l=mid+1,rt=rt<<1|1;}
}
update(root[rt],1,n,L,R);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int jd;
scanf("%d%d%d%d",&jd,&a,&b,&c);
if(jd==1)
{
insert(a,b);
}else printf("%d\n",work(a,b));
}
}