【CF1181D】Irrigation

题目大意:给定 M 个城市,每年会选出一个城市举办比赛,现给出前 N 年城市举办比赛的情况。在接下来的年份中,每年会在举办比赛次数最小的城市举办比赛,如果有很多城市举办次数均为最小值,则在编号最小的城市举办比赛。现给出 Q 个询问,每次询问第 K 年在哪个城市举办比赛。

题解:
首先,记录下每个城市前 N 年举办比赛的次数,并按照举办比赛的次数为第一关键字,城市编号为第二关键字进行从小到大排序。
发现排序后城市举办比赛的情况如下图所示

那么若当前矩形高度小于下一个小矩形高度时,一定是当前矩形高度增加,且对于大矩形内的小矩形高度呈现周期性增加。
因此,可以采用将询问离线,并将问题划分为前 i 个高度均为 h[i] 的矩形到前 i+1 个高度均为 h[i+1] 的矩形的时间。对于每个划分出来的子问题,i 个小矩形高度呈现周期性变化,可以利用取模操作得到第几个矩形高度增加。需要注意的是,这里面的第 i 个矩形为相对下标,还需要转化成具体是第几个城市,这里可以采用权值线段树上二分的操作,求出第 k 个数对应的下标即可。

代码如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
typedef long long LL;

int n,m,Q,sz[maxn<<2],ans[maxn];
struct city{int cnt,id;}c[maxn];
struct qry{int id;LL year;}q[maxn];
void insert(int o,int l,int r,int pos){
	if(l==r){++sz[o];return;}
	int mid=l+r>>1;
	if(pos<=mid)insert(o<<1,l,mid,pos);
	else insert(o<<1|1,mid+1,r,pos);
	sz[o]=sz[o<<1]+sz[o<<1|1];
}
int kth(int o,int l,int r,int k){
	if(l==r)return l;
	int mid=l+r>>1;
	if(k<=sz[o<<1])return kth(o<<1,l,mid,k);
	else return kth(o<<1|1,mid+1,r,k-sz[o<<1]);
}

void read_and_parse(){
	scanf("%d%d%d",&n,&m,&Q);
	for(int i=1;i<=n;i++){
		int x;scanf("%d",&x);
		++c[x].cnt;
	}
	for(int i=1;i<=m;i++)c[i].id=i;
	for(int i=1;i<=Q;i++){
		LL year;scanf("%lld",&year);
		q[i].id=i,q[i].year=year;
	}
	sort(c+1,c+m+1,[](const city &a,const city &b){
		return a.cnt==b.cnt?a.id<b.id:a.cnt<b.cnt;
	});
	sort(q+1,q+Q+1,[](const qry &a,const qry &b){
		return a.year<b.year;
	});
}
void solve(){
	LL now=n;
	for(int i=1,j=1;i<=m;i++){
		LL nxt=i==m?2e18:(LL)(c[i+1].cnt-c[i].cnt)*i+now;
		insert(1,1,m,c[i].id);
		while(j<=Q&&q[j].year<=nxt){
			LL year=q[j].year;
			int idx=(year-now)%i;
			if(idx==0)idx=i;
			ans[q[j].id]=kth(1,1,m,idx);
			++j;
		}
		now=nxt;
	}
	for(int i=1;i<=Q;i++)printf("%d\n",ans[i]);
}
int main(){
	read_and_parse();
	solve();
	return 0;	
} 
posted @ 2019-06-17 14:36  shellpicker  阅读(519)  评论(0编辑  收藏  举报