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
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
2
1
HINT
【样例说明】
第一个操作:位置1的数只有1, 位置2的数也只有 1 。 第二个操作:后位置1的数有1 、2,位置2的数也有 1 、 2 。 第三次询问:位置1到位置1第2大的数是1 。 第四次询问:位置1到位置1第1大的数是 2 。 第五次询问:位置1到位置2第3大的数是 1 。
N,M<=50000,N,M<=50000
a<=b<=N
1操作中abs(c)<=N
2操作中c<=Maxlongint
Source
分析: 树套树(主席树+线段树?)
不,我们用整体二分
之前做过K大数:poj2104
这道题的不同之处就在于修改的是一个区间
所以我们考虑用线段树代替树状数组
询问的是第K大:从大到小排序后,排名第K的数
二分答案M,每次把插入值大于M的修改插入到线段树中
处理完询问后,不要忘了把消除区间影响
tip
所有的操作有一种潜在的规律:时间
一开始把t1,t2写错了(手癌晚期。。。药丸)
< 还是 <= ,> 还是 >=,要想明白(暴力对着样例调一调吧)
开ll
对于消除区间影响,学姐有一种很好的方法:
因为每次solve的时候线段树中是没有修改值的(所有值都是0)
我采用的方法是修改了哪些区间,就消除哪些区间的影响
实际上我们可以直接在根结点上打一个清空标记
从上到下访问的时候,pushdown即可
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const ll INF=1e9;
const int N=500005;
struct node{
int x,y,type,id,num;
ll z;
};
node a[N],q1[N],q2[N];
int n,m,tt=0;
ll maxx,minn,ans[N],tree[N<<2],ad[N<<2];
void push(int bh,int l,int r)
{
int L=bh<<1,R=bh<<1|1,mid=(l+r)>>1;
if (ad[bh]!=0&&l!=r)
{
tree[L]+=(ll)(mid-l+1)*ad[bh];
tree[R]+=(ll)(r-mid)*ad[bh];
ad[L]+=ad[bh]; ad[R]+=ad[bh];
ad[bh]=0;
}
}
ll ask(int bh,int l,int r,int L,int R)
{
push(bh,l,r);
if (l>=L&&r<=R)
return tree[bh];
int mid=(l+r)>>1;
ll ans=0;
if (L<=mid) ans+=ask(bh<<1,l,mid,L,R);
if (R>mid) ans+=ask(bh<<1|1,mid+1,r,L,R);
return ans;
}
void add(int bh,int l,int r,int L,int R,ll z)
{
push(bh,l,r);
if (l>=L&&r<=R)
{
tree[bh]+=(ll)(r-l+1)*z;
ad[bh]=z;
return;
}
int mid=(l+r)>>1;
if (L<=mid) add(bh<<1,l,mid,L,R,z);
if (R>mid) add(bh<<1|1,mid+1,r,L,R,z);
tree[bh]=tree[bh<<1]+tree[bh<<1|1];
}
void solve(int l,int r,ll L,ll R)
{
if (L==R)
{
for (int i=l;i<=r;i++)
if (a[i].type==2) ans[a[i].id]=L;
return;
}
ll M=(L+R)>>1;
int t1=0,t2=0;
for (int i=l;i<=r;i++)
{
if (a[i].type==1) //insert
{
if (a[i].z>M) //
{
add(1,1,n,a[i].x,a[i].y,1);
q2[++t2]=a[i];
}
else q1[++t1]=a[i];
}
else
{
ll sum=ask(1,1,n,a[i].x,a[i].y);
if (sum<a[i].z) a[i].z-=sum,q1[++t1]=a[i]; //比M大的太少了
else q2[++t2]=a[i];
}
}
for (int i=1;i<=t1;i++) a[l+i-1]=q1[i];
for (int i=1;i<=t2;i++) a[l+t1+i-1]=q2[i];
for (int i=l+t1;i<=r;i++) if (a[i].type==1) add(1,1,n,a[i].x,a[i].y,-1); //清除影响
solve(l,l+t1-1,L,M);
solve(l+t1,r,M+1,R);
}
int main()
{
scanf("%d%d",&n,&m);
maxx=-INF; minn=INF;
for (int i=1;i<=m;i++)
{
scanf("%d%d%d%lld",&a[i].type,&a[i].x,&a[i].y,&a[i].z);
if (a[i].type==1) maxx=max(maxx,a[i].z),minn=min(minn,a[i].z);
if (a[i].type==2) a[i].id=++tt;
}
solve(1,m,minn,maxx);
for (int i=1;i<=tt;i++) printf("%lld\n",ans[i]);
return 0;
}