BZOJ3110:[Zjoi2013]K大数查询
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\n",ans[i]); } int main(){ work(); return 0; }