BZOJ3110:[Zjoi2013]K大数查询

3110: [Zjoi2013]K大数查询

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 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

Sample Output

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;
}

 

posted @ 2017-07-12 22:10  QYP_2002  阅读(192)  评论(1编辑  收藏  举报