【CF 1181D】Irrigation(离线+动态第k大)

CF1181D

题意

m m m个城市,已经举办过 n n n次比赛,接下来比赛举办地的规则为:选出举办过比赛次数最少且编号最小的城市举办比赛。有 q q q次询问,问第 k i k_i ki届比赛在那个城市举行。
1 ≤ n , m , q ≤ 500000 , n + 1 ≤ k i ≤ 1 0 18 1≤n,m,q≤500000, n+1\le k_i \le 10^{18} 1n,m,q500000,n+1ki1018

解法

询问离线,并升序排序。城市按照举办次数升序排序,计算相邻两个对总举办数的贡献,在总数以内的答案可以计算出来城市编号的排名,用权值线段树等数据结构维护即可(代码采用权值树状数组)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1<<19;

int a[N],r[N];
bool cmp(int x,int y){
	if(a[x]==a[y])return x<y;
	return a[x]<a[y];
}
int t[N];
int find(int x){
    int p=N>>1,ans=0;
    for(int lb=p;lb;){
        lb>>=1;
        if(t[p]>=x)p-=lb;
        else {ans=p;x-=t[p];p+=lb;}
    }
    return ans+1;
}
void add(int p){
	for(;p<N;p+=(p&-p))t[p]++;
}
struct que{
	ll k;
	int i;
	bool operator<(que b)const{
		return i<b.i;
	}
}qs[N];
bool cmpq(que x,que y){
	return x.k<y.k;
}
int main(){
	ios::sync_with_stdio(0);
	int n,m,q;
	cin>>n>>m>>q;
	for(int i=1;i<=m;i++)r[i]=i;
	for(int i=0,x;i<n;i++){
		cin>>x;a[x]++;
	}
	sort(r+1,r+m+1,cmp);
	for(int i=0;i<q;i++){
		cin>>qs[i].k;
		qs[i].i=i;
	}
	sort(qs,qs+q,cmpq);
	int l=0;
	ll s=n;
	for(int i=1;i<=m;i++){
		ll sx=s;
		if(i==m)s=2e18;
		else s+=ll(a[r[i+1]]-a[r[i]])*i;
		add(r[i]);
		while(l<q && qs[l].k<=s){
			qs[l].k=find((qs[l].k-sx-1)%i+1);
			l++;
		}
	}
	sort(qs,qs+q);
	for(int i=0;i<q;i++)
		cout<<qs[i].k<<'\n';
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值