3110: [Zjoi2013]K大数查询
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
分析:
还是一个整体二分的做法,对于区间更新,用树状数组或者线段树去维护
对于c可以全部+N+1转换成正数方便处理,负数在区间直接/2会出bug,在结果处将答案转换过来
另外就是要开long long
说实在的整体二分对手速的要求和仔细程度还真是高,毕竟是离线的不是很好找bug
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
#define maxn 50005
int n,m;
long long ans[maxn];
struct node
{
int kind;
int l,r;
long long k;
int index;
} q[maxn],q1[maxn],q2[maxn];
long long c1[maxn],c2[maxn];
inline int lowbit(int x)
{
return x&-x;
}
inline void update(int x,long long val)
{
for(int i=x; i<=n; i+=lowbit(i))
{
c1[i]+=val;
c2[i]+=(x-1)*val;
}
}
inline long long query(int x)
{
long long sum1=0,sum2=0;
for(int i=x; i>0; i-=lowbit(i))
{
sum1+=c1[i];
sum2+=c2[i];
}
return sum1*x-sum2;
}
void divide(int s,int t,long long l,long long r)
{
if(s>t) return ;
if(l==r)
{
for(int i=s; i<=t; i++)
{
if(q[i].kind==2)
{
ans[ q[i].index ]=l;
}
}
return ;
}
long long mid=(l+r)/2;
int num1=0,num2=0,flag1=0,flag2=0;
for(int i=s; i<=t; i++)
{
if(q[i].kind==1)
{
if(q[i].k<=mid)
{
update(q[i].l,1);
update(q[i].r+1,-1);
q1[num1++]=q[i];
}
else q2[num2++]=q[i];
}
else
{
long long tmp=query(q[i].r)-query(q[i].l-1);
if(tmp>=q[i].k) q1[num1++]=q[i],flag1=1;
else q[i].k-=tmp,q2[num2++]=q[i],flag2=1;
}
}
for(int i=s; i<=t; i++)
{
if(q[i].kind==1)
{
if(q[i].k<=mid)
{
update(q[i].l,-1);
update(q[i].r+1,1);
}
}
}
for(int i=0; i<num1; i++) q[i+s]=q1[i];
for(int i=0; i<num2; i++) q[i+s+num1]=q2[i];
if(flag1) divide(s,s+num1-1,l,mid);
if(flag2) divide(s+num1,t,mid+1,r);
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(c1,0,sizeof c1);
memset(c2,0,sizeof c2);
int num=0;
for(int i=1; i<=m; i++)
{
scanf("%d%d%d%lld",&q[i].kind,&q[i].l,&q[i].r,&q[i].k);
if(q[i].kind==1)
{
q[i].k+=n+1;
q[i].k=2*n+2-q[i].k;
}
else if(q[i].kind==2) q[i].index=num++;
}
divide(1,m,1,2*n+2);
for(int i=0; i<num; i++)
{
printf("%lld\n",(long long)n+1-ans[i]);
}
}
return 0;
}