GCJ 2016 Qual Round D - Fractiles
問題
Dashboard - Qualification Round 2016 - Google Code Jam
問題概要
gold(G)のタイルとlead(L)のタイルが一直線上に並んだものがある.それはフラクタル構造を持っており,次のような規則を満たしている:
2つのパラメータによってフラクタルは決定づけられる.長さのoriginal sequenceと,その複雑さである.複雑さ1のものをoriginal sequenceとして,複雑さのsequenceは複雑さのsequenceを基にして再帰的に構成される.sequenceを線形に見ていき:
- Lの位置はoriginal sequenceに置換
- Gの位置は個のGに置換
を繰り返していく(問題文の図を参照).
いま,このフラクタルを発見したが,汚れていてそれぞれのタイルがgoldなのかleadなのか分からないが,との値についてはわかっている.そして今,タイルを枚磨いて,そのタイルがどちらであるかを調べることが出来る.この時,original sequenceがいかなるものであったとしても,そのフラクタル内にgoldが1つでも入っているかどうかが分かるようなタイルの磨き方はあるだろうか?
ある場合には枚以下のタイルの磨くべき位置を,そのような方法がない場合にはIMPOSSIBLE
と答えよ.
アイデア
smallに関してはなので,はじめから枚を調べればもしoriginalの1枚目がLだったなら,original sequence全体を確認できることになるのでそこでGがあるかないかが判定できる.GならGがあるとわかるので問題ない.
問題はlargeである.今回は調べられるタイルの枚数がとなっている.まず,少なくとも1つはフラクタル内にGが含まれるならば,original sequence内にGが少なくとも1つあるということは対偶(original sequence内が全てLなら,フラクタル内は全てLになる)を考えれば明らか.
original sequenceをと固定し,複雑さのフラクタルをと表すことにしよう.もちろん. そして,との関係について考えよう.の左から番目の位置に対して,そこがならばの左から番目の位置との左から番目の位置が一致することを表す.また,そこがならばの左から番目の位置は全てになる.これは定義どおりなので明らかだ.
ここから推論すると,逆にの左から番目がであるというのは,「の左から番目がであり,かつの左から番目がである」という時のみに起こることだということになる.これをさらに掘り下げると,のある位置に関してであるというのは,の全ての位置の中である特定の部分集合がすべてになっているということに帰結する.
例えばのとき,の8番目はの3番目から派生する位置で,更にそこはの1番目から派生する位置である.ということはの8番目がであるには,この派生前の位置もでないといけない,更にこれらは全てのいずれかの位置に対応しているので,結局順番に見ていくとの8番目がであるにはの2,3,1番目がそれぞれでないといけないことが分かる(つまり全部).この結果から,の時には左から8番目を調べることによって全体に少なくとも1つがあるかないかを調べることが可能になる.
これを一般化してみる.ますはの文字目からスタートすることにしよう.そして,その文字から生成されるもののうち,左から番目の子孫を選ぶことにしよう(これは上の話).そして,その位置から生成されるもののうち,左から番目の子孫を選ぶことにしよう.と,これを続けていき,内において前段階で生成された子孫のうち左から番目の子孫を選ぶことにしよう.こうして選ばれた内のとある1文字を調べることによって内の番目に少なくとも1つはが含まれているかどうかを判定することが可能になる.つまり,それぞれのが被らないようにうまく選ぶことによって,1箇所を調べることによって内の箇所をチェックすることが可能であるということになる.内を全て調べるには,の長さはであるから,のときは調べることが不可能,よってIMPOSSIBLE
ということになる.逆にそうでない時は調べ尽くすことが可能である.調べる可能なエリア個に対して,1つ目の場所での文字目をチェックできる位置,2つ目の場所で文字目をチェックできる位置,個目の場所で文字目をチェックできる位置を選べば良い.
さて,ここで確認しておくとにおいて文字目の子孫となるものは内において左から文字目という形で表わされるのであった.あとはこれを実装して,調べるべき位置個を出力してあげれば良い.
実装(C++)
#include <bits/stdc++.h> using namespace std; typedef long long ll; #define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i)) #define each(itr,c) for(__typeof(c.begin()) itr=c.begin(); itr!=c.end(); ++itr) #define all(x) (x).begin(),(x).end() #define mp make_pair #define pb push_back #define fi first #define se second int main() { int T; cin >>T; rep(t,T) { ll k,c,s; cin >>k >>c >>s; printf("Case #%d:",t+1); if(s*c<k) printf(" IMPOSSIBLE\n"); else { vector<ll> ans; for(ll i=1; i<=k; i+=c) { ll p=i; rep(j,c-1) p=(p-1)*k+min(i+j+1,k); ans.pb(p); } for(const auto &x:ans) cout << " " << x; cout << endl; } } return 0; }
コードに落とすととてつもなくシンプル...