外层权值线段树,对于每个权值线段树节点,建立区间线段树。但是内层这样普通建树会TLE&&MLE。仔细想会发现,区间线段树不用都建出来,用到哪个点就开哪一个点,每次操作最多经过logn个权值线段树节点,访问每个权值线段树节点时,最多修改logn个区间线段树节点,所以区间线段树总节点个数nlog²n。注意longlong。
#include<iostream>
#include<cstdio>
#include<algorithm>
#define LL long long
#define ls tree[x].ch[0]
#define rs tree[x].ch[1]
using namespace std;
int n,m,len;
void read(int &a)
{
int h=1;a=0;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-') h=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
a*=10;a+=c-'0';
c=getchar();
}a*=h;
}
int tot;
struct xds{
int ch[2];
LL cnt;
int lazy;
}tree[15000000];
int L,R;
void spread(int x,int l,int r)
{
if(tree[x].lazy)
{
int mid=(l+r)>>1;
if(!ls) ls=++tot;
tree[ls].cnt+=(mid-l+1)*tree[x].lazy;
tree[ls].lazy+=tree[x].lazy;
if(!rs) rs=++tot;
tree[rs].cnt+=(r-mid)*tree[x].lazy;
tree[rs].lazy+=tree[x].lazy;
tree[x].lazy=0;
}
}
void up(int x)
{tree[x].cnt=tree[ls].cnt+tree[rs].cnt;}
void add(int x,int l,int r)
{
if(l>=L&&r<=R)
{
tree[x].cnt+=r-l+1;
tree[x].lazy++;
return ;
}
if(!ls) ls=++tot;
if(!rs) rs=++tot;
spread(x,l,r);
int mid=(l+r)>>1;
if(mid>=L)add(ls,l,mid);
if(mid<R) add(rs,mid+1,r);
up(x);
}
struct qzs{
int rt;
}tr[400005];
void insert(int x,int l,int r,int c)
{
if(!tr[x].rt) tr[x].rt=++tot;
add(tr[x].rt,1,n);
if(l==r) return ;
int mid=(l+r)>>1;
if(c<=mid) insert(x<<1,l,mid,c);
else insert(x<<1|1,mid+1,r,c);
}
LL ask(int x,int l,int r)
{
if(!x) return 0;
if(l>=L&&r<=R)
return tree[x].cnt;
int mid=(l+r)>>1;
LL ans=0;
spread(x,l,r);
if(mid>=L) ans+=ask(tree[x].ch[0],l,mid);
if(mid<R) ans+=ask(tree[x].ch[1],mid+1,r);
return ans;
}
int Ask(int x,int l,int r,LL k)
{
if(l==r) return l;
int mid=(l+r)>>1;
LL cnt=ask(tr[x<<1|1].rt,1,n);
if(cnt<k)
return Ask(x<<1,l,mid,k-cnt);
return Ask(x<<1|1,mid+1,r,k);
}
int main()
{
scanf("%d%d",&n,&m);
int op,a,b,c;
len=2*n+1;
for(int i=1;i<=m;i++)
{
read(op);read(a);
read(b);read(c);
L=a,R=b;
if(op==1)
insert(1,1,len,c+n+1);
if(op==2)
printf("%d\n",Ask(1,1,len,c)-n-1);
}
}