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
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
HINT
N,M<=50000,N,M<=50000a<=b<=N
1操作中abs(c)<=N
2操作中abs(c)<=Maxlongint
题解
树套树做法,第一位权值,第二位区间
对于区间[l,r],将权值在此之内的修改建立一棵普通线段树。这样对于一个询问,就可以类似二分答案,首先看权值在[1,mid]中有几个在询问的区间中,如果<排名,就往右,否则往左。
注意用永久化标记。否则会T。
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
#define N 25000005
ll sum[N];
int lazy[N],ls[N],rs[N],root[500005];
int cnt,a,b,c,n,m;
using namespace std;
inline int read()
{
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
inline void update(int p,int l,int r)
{
sum[p]=sum[ls[p]]+sum[rs[p]]+lazy[p]*(r-l+1);
}
void change(int &p,int l,int r)
{
if (!p) p=++cnt;
if (l>=a&&r<=b){lazy[p]++;sum[p]+=r-l+1;return;}
int mid=(l+r)>>1;
if (a<=mid) change(ls[p],l,mid);
if (b>mid) change(rs[p],mid+1,r);
update(p,l,r);
}
void insert()
{
int p=1,l=1,r=n*2+1;
while (l!=r)
{
change(root[p],1,n);
int mid=(l+r)>>1;
if (c<=mid)r=mid,p=p*2;else l=mid+1,p=p*2+1;
}
change(root[p],1,n);
}
ll query(int p,int l,int r)
{
if (!p) return 0;
if (l>=a&&r<=b)return sum[p];
int mid=(l+r)>>1;
ll sum=0;
if (a<=mid) sum+=query(ls[p],l,mid);
if (b>mid) sum+=query(rs[p],mid+1,r);
return sum+lazy[p]*(min(b,r)-max(l,a)+1);
}
ll solve()
{
int p=1,l=1,r=n*2+1;
while (l!=r)
{
int mid=(l+r)>>1;
ll num=query(root[p*2],1,n);
if (num>=c)p=p*2,r=mid;else p=p*2+1,l=mid+1,c-=num;
}
return n-l+1;
}
int main()
{
n=read();m=read();
while (m--)
{
int opt=read();
a=read();b=read();c=read();
if (opt==1)
{
c=n-c+1;
insert();
}
else
{
printf("%lld\n",solve());
}
}
return 0;
}