mini notes

競技プログラミングの解法メモを残していきます。

DDCC2019Final B - 大吉数列 (Array of Fortune) (600)

B - 大吉数列 (Array of Fortune)

概要

整数N, K, Rが与えられる。次の条件を満たす数列Aが存在すればその数列を1つ出力し、存在しなければNo Luckと出力せよ。

  • 数列には1からNまでの各数字がちょうど1回現れる
  • A_{i} \geq A_{j} + K を満たす(i,j)の組(i < j)がちょうどR個存在する
制約

1 \leq N \leq 100000
1 \leq K \leq N-1
1 \leq R \leq N \times (N-1) / 2

方針

極端な数列を考える。
数列\{1,2, \cdots, N\}は全てのi < jについて、A_{i} \geq A_{j} + K', K' \geq1を満たすK'が存在しない。
また数列\{N,N-1, \cdots, 1\}は全てのi < jについて、A_{i} \geq A_{j} + K', K' \geq1を満たすK'が存在する。

後者の数列が各Kに対するRの上限を与える。
すなわちK=N-1ならR \leq 1(A_{i},A_{j}) = (N, 1))、K=N-2ならR \leq 1+2 = 3(A_{i},A_{j}) = (N, 1),\  (N-1, 1),\ (N, 2))、のように計算される。

もし、与えられたRがこの上限より大きければ、数列を構成できないためNo Luckとなる。
与えられたRがこの上限以下であれば、数列を構成できる。

必要な数列は\{N,N-1, \cdots, 1\}を加工していくことで構成できる。
Rの上限と与えられたRとの差をremとし、rem0にすることを考える。
\{N,N-1, \cdots, 1\}と、そこからxを先頭にした\{x, N,N-1, \cdots,x+1, x-1, \cdots 1\}を比較すると、
後者の方がA_{i} \geq A_{j} + K, K \geq1を満たす(i,\ j)\ (i < j)の組の数がN- (x + K) + 1減る。
(N, x),\ (N-1,x)\ , \cdots , (x+K, x)の組がなくなるため)

よって数列を後ろから見てゆき、減らす組の数がrem以下であるなら、その数を移動させてremを減らす。
すでに移動された数がある場合、移動先を最後に移動させた数の直後にすると、remを増やすことがない。

解答

Submission #4046606 - DISCO presents ディスカバリーチャンネル コードコンテスト2019 本戦

#include <bits/stdc++.h>
 
#define FOR(i,a,b) for(int i=(a);i<(b);i++)
#define rep(i,n) FOR(i,0,n)
#define RFOR(i,a,b) for(int i=(a)-1;i>=(b);i--)
#define rrep(i,n) RFOR(i,n,0)
 
using namespace std;
 
typedef long long ll;
typedef unsigned long long ull;
 
int main()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
 
	ll N, K, R;
	cin >> N >> K >> R;
 
	ll up[N+1];
	up[N] = 0;
	for(ll i = 1; i <= N; i++){
		up[N-i] = up[N-i+1] + i;
	}
 
	// for(ll k = 1; k <= N-1; k++){
	// 	cout << up[k] << " ";
	// }
	// cout << endl;
 
	if(R > up[K]){
		cout << "No Luck" << endl;
		return 0;
	}
 
	vector<ll> v;
	for(ll i = N; i >= 1; i--){
		v.push_back(i);
	}
 
	vector<ll> w;
	ll rem = up[K] - R;
	for(ll i = N-1; i >= 0; i--){
		if(i + 1 - K > 0 && rem - (i + 1 - K) >= 0){
			// cout << i << " " << rem << endl;
			w.push_back(N-i);
			rem -= i + 1 - K;
			v.erase(v.begin() + i);
		}
	}
 
	rep(i, w.size()){
		cout << w[i] << (v.size() == 0 ? "": " ");
	}
	rep(i, v.size()){
		cout << v[i] << ((i == v.size()-1) ? "": " ");
	}
	cout << endl;
}