Codeforces 1181D Irrigation 离线+权值线段树求第k大

传送门

思路: 首先根据举办次数从小到大排序,然后对询问进行离线(按照ki的权值从小到大排序), 对于每个k询问,我们去看是否满足cnt[i+1].num*i-sum[i]<=k, (sum[i]代表1到i的举办总次数,cnt[i+1].num*i代表从1到i这i个地点举办次数都达到cnt[i+1].num而举办的总场数), 若<=k则将k%i,然后在权值线段树中查询第k大的数字(注意k为0则将k设为i), 若>k则将cnt[i+1].id更新到权值线段树中,并更新sum.

 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define mem(a,x) memset(a,x,sizeof(a))
#define gi(x) scanf("%d",&x)
#define gi2(x,y) scanf("%d%d",&x,&y)
#define gi3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define gll(x) scanf("%lld",&x)
#define gll2(x,y) scanf("%lld%lld",&x,&y)
using namespace std;
const double eps=1e-8; 
typedef long long ll;
const int MAXN=500005;
const ll mod=1e9+7;
const int inf=0x3f3f3f3f;
struct Node{
	int l,r;
	int val;
}node[MAXN<<2];
struct Query{
	int id;
	int ans;
	ll k;
}query[MAXN];
void pushup(int i){
	node[i].val=node[i<<1].val+node[i<<1|1].val;
}
void build(int l,int r,int i){
	node[i].l=l;
	node[i].r=r;
	node[i].val=0;
	if(l==r)return;
	int mid=(l+r)/2;
	build(l,mid,i<<1);
	build(mid+1,r,i<<1|1);
}
void update(int i,int x){
	if(node[i].l==node[i].r){
		node[i].val=1;
		return ;
	}
	int mid=(node[i].l+node[i].r)/2;
	if(x<=mid){
		update(i<<1, x);
	}
	else{
		update(i<<1|1, x);
	}
	pushup(i);
}
int qry(int i,int x){// x 大
	if(node[i].l==node[i].r){
		return node[i].l;
	}
	int t=i<<1;
	if(node[t].val>=x){
		return qry(t, x);
	}
	else return qry(t|1,x-node[t].val);
}
int n,m,q;
struct Num{
	int num;
	int id;
}cnt[MAXN];
bool cmp(Query a,Query b){
	return a.k1ll*cnt[ix+1].num*ix-sum){
			ix++;
			sum+=cnt[ix].num;
			update(1, cnt[ix].id);
		}
		k-=1ll*cnt[ix].num*ix-sum;
		k%=ix;
		if(k==0)k=ix;
		query[i].ans=qry(1, int(k));
	}
	sort(query+1,query+q+1,cmp1);
	for(int i=1;i<=q;i++){
		printf("%d\n",query[i].ans);
	}
}

 

你可能感兴趣的:(线段树,离线)