kmjp's blog

競技プログラミング参加記です

Codeforces #567 Div2 D. Irrigation

まずいこれ3か月以上前の問題じゃないか。
https://codeforces.com/contest/1181/problem/D

問題

N個の町があり、過去コンテストの開催回数が与えられる。
これから何度もコンテストを行うわけだが、今後コンテストの開催地は以下の通り選ばれる。

  • 今まで行った回数が最小の町のうち、最小の番号の町

Q個のクエリが与えられる。
各クエリでは、今からK回目のコンテストがどの町で行われるかを求めよ。

解法

過去の開催回数は各町500000以下である。
よって、全町の開催回数が500000に到達するまで、シミュレートしよう。
開催がx回目に並ぶタイミングでは、過去の開催回数がx回未満のものが開催対象になる。
その間に実行対象となるクエリに対しては、対象の町の数をBITやSegTreeで管理しておき、2分探索することで答えることができる。

それ以降は全町で順番に開催される。

int N,M,Q;
int C[505050];
vector<int> A[505050];
set<pair<ll,int>> S;

template<class V, int ME> class BIT {
public:
	V bit[1<<ME],val[1<<ME];
	V operator()(int e) {if(e<0) return 0;V s=0;e++;while(e) s+=bit[e-1],e-=e&-e; return s;}
	void add(int e,V v) { val[e++]+=v; while(e<=1<<ME) bit[e-1]+=v,e+=e&-e;}
	void set(int e,V v) { add(e,v-val[e]);}
	int lower_bound(V val) {
		V tv=0; int i,ent=0;
		for(i=ME-1;i>=0;i--) if(tv+bit[ent+(1<<i)-1]<val) tv+=bit[ent+(1<<i)-1],ent+=(1<<i);
		return ent;
	}
};
BIT<int,20> bt;

int ret[505050];

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	scanf("%d%d%d",&N,&M,&Q);
	FOR(i,N) {
		scanf("%d",&x);
		C[x]++;
	}
	for(i=1;i<=M;i++) A[C[i]].push_back(i);
	FOR(i,Q) {
		ll d;
		scanf("%" PRId64 ,&d);
		S.insert({d,i});
	}
	ll did=N;
	int tot=0;
	for(i=1;i<=500001;i++) {
		FORR(v,A[i-1]) bt.add(v,1), tot++;
		if(S.empty()) break;
		while(S.size() && S.begin()->first<=did+tot) {
			auto p=*S.begin();
			S.erase(S.begin());
			ret[p.second]=bt.lower_bound(p.first-did);
		}
		did+=tot;
	}
	while(S.size()) {
		auto p=*S.begin();
		S.erase(S.begin());
		ret[p.second]=(p.first-1-did)%M+1;
	}
	
	FOR(i,Q) _P("%d\n",ret[i]);
	
	
}

まとめ

CodeforcesのDiv2はこれ系の問題多い気がする。