3110: [Zjoi2013]K大数查询
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 8652 Solved: 2608
[Submit][Status][Discuss]
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
思路{
大意:有N个位置,M个操作。操作有两种,每次操作如果是1
a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c.如果是2
a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
思路:可以树套树解决这个问题,也可以整体二分。
何谓整体二分?就是对于多个询问操作,二分答案,但在二分答案的时候,传参当前答案的对于每组询问的可行范围。对当前若干个询问或者操作进行和二分的答案mid的比对,判断丢到左边一段的二分或者是右边一段的二分,得出每次二分答案的判定范围。然后递归求解(纯属个人理解)
对于这道题:二分答案,如果当前插入操作的值大于mid应当记录这一个,可以线段树,也可以带区间修改的树状数组。把它加入哪一段呢?思考:若大于mid说明他能够影响更大的解,所以把它加入右端待询问的区间。(即递归下去为l=mid);反之小于mid加入左端待选区间(r=mid)。对于查询操作,由于加入顺序满足时间顺序,无需考虑冲突,所以查询待询问区间中大于mid的数有多少个。若大于了K,说明二分的答案mid小了。
把它丢到右端待询问的区间.(即递归下去为l=mid);反之小于mid加入左端待选区间(r=mid *在这个地方需要记录一下贡献值!)。然后清空本次树状数组或线段树。递归解两个子问题,知道二分答案的左
右端点重合,就记录当前所有询问的答案即可。
}
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define inf (1<<30)
#define il inline
#define RG register
#define LL long long
#define N 500010
using namespace std;
struct ASK{int l,r,id,flag,k;}ask[N];
int n,sum,m,ans[N];LL tree1[N],tree2[N];
#define lowbit(o) o&(-o)
void Insert(int x,int y){for(int i=x;i<=n;i+=lowbit(i))tree1[i]+=y,tree2[i]+=(LL)x*y;}
LL query(int x){LL aa=0;for(int i=x;i;i-=lowbit(i))aa+=tree1[i]*(x+1)-tree2[i];return aa;}
ASK quel[N],quer[N];
il void solve(int head,int tail,int L,int R){
if(L==R){
for(RG int i=head;i<=tail;++i)ans[ask[i].id]=L;
return;
}int mid=(L+R+1)>>1;int ln=0,rn=0;bool ll=0,rr=0;
for(RG int i=head;i<=tail;++i){
if(ask[i].flag==1){
if(ask[i].k>=mid){
Insert(ask[i].l,1),Insert(ask[i].r+1,-1);
quer[++rn]=ask[i];
}
else quel[++ln]=ask[i];
}
else {
LL x=query(ask[i].r)-query(ask[i].l-1);
if(x>=ask[i].k)quer[++rn]=ask[i],rr=1;
else ll=1,ask[i].k-=x,quel[++ln]=ask[i];
}
}for(RG int i=head;i<=tail;++i)if(ask[i].flag==1&&ask[i].k>=mid)Insert(ask[i].l,-1),Insert(ask[i].r+1,1);
for(RG int i=1;i<=ln;++i)ask[head+i-1]=quel[i];
for(RG int i=1;i<=rn;++i)ask[head+ln+i-1]=quer[i];
if(ll)solve(head,head+ln-1,L,mid-1);
if(rr)solve(head+ln,tail,mid,R);
}
il void work(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i){
scanf("%d%d%d%d",&ask[i].flag,&ask[i].l,&ask[i].r,&ask[i].k);
if(ask[i].flag==2)ask[i].id=++sum;
}
solve(1,m,1,n);
for(int i=1;i<=sum;++i)printf("%d
",ans[i]);
}
int main(){
work();
return 0;
}