tozangezan's diary

勝手にソースコードをコピペして利用しないでください。

競技プログラミングをやっている人に読んでほしいかと言われればそうだと思うが実際あんまり関係のないなぐり書き

これはsugimさんが数分前に言及していたCompetitive Programming Advent Calendar 2016(不真面目)の一環です。
誰かカレンダーを作ってくれたら適当な日にリンクはっつけられます


競技プログラミングの"まわり"の話。

もう個々のテクニックについて書いてもしょうがないし取り組み方ですらそこらへんに転がってるし振り返りすら最近はいくらでもあって適当に切りはりしてつなげれば誰のでも再現できるような気がするので、競技プログラミング競技プログラミングでない部分についてダラダラかきなぐります。

そもそも

僕は農学部生態学をやっています。おっと、農学部生態学をやっている競技プログラマーは世界中どこを探しても他にいないだろうし、対象者がいなくなってしまいました。ここまで読んで頂いてありがとうございました。明日の記事は 1_000_000_007 さんで「悪問の具体的事例とどうすればその問題をよりよくできるか」です。楽しみですね。*1

インターンの面接にどうすれば受かるのだろうか?という話

↑みたいな人が情報系企業を体験して選択肢の取捨選択をするのにはインターンが都合良いと思うんですが、人混みが耐えがたい人(はっきり言って東京の人の多さは異常です / なんで東京23区より人口密度の高いソウルですらあれほど人で混み合っていないのか、理由は単純で、東京はその外に百万都市ですらごろごろ(横浜、横浜、横浜、横浜、川崎、さいたま、千葉と周辺)転がっていてそういう人たちが一斉に東京に押し寄せてくるからです。)は必然的に面接で英語が必要になります。正直やる気があるレッドコーダーなら他は余裕じゃないでしょうか。FHCのFinalistが面接で落ちるとか多分普通の人はやらかさないと思います。そして今年はDCJのFinalistでしたがどうなることやら...

で結局もう2年近くインターンの面接を通ることを目指して(+研究生活のためにも)英語をやっているのですがあまりに厳しいです。
英語 - Google スプレッドシート

他としてIndeedみたいないい社が地方都市にあるとすごくいいなって思ってます(でもIndeedはアルゴリズムバンバン感があるのか微妙...)

さらに競プロから離れる

そういえばそもそも海外のtech companyにインターンに行くには(大概アルゴリズムもろくに知らないような人が書いてる話とかで知りたいことが得られることが皆無、唯一面白いと思ったのは英語力だけが足りない中国人があらかじめ面接で想定される質問の答えを全部作ってあとは本番で読むだけにすることで面接を突破しようとしていた話)?だったり海外の大学院に行くには?みたいな話、日本の情報が異様に少ないのは何なんですか。韓国語で検索したほうが見つかるので時折韓国語で検索しているのですが。

想像以上にこの世界はレーティング至上主義なんだという話

↑に書いたように多分アルゴリズム力がTopCoderの2200の人も2800の人もましてや3500の人も多分普通にエンジニアをするには何ら変わりはないと思うんですけど、(まあ賞金で稼げる3500の人とかは別として)コミュニティー内の変な裏のつながりで得られるものってかなり変わってくると思うんですよね。例えばatcoderのwriterとかtesterはお金がいくらもらえるのか?そもそもお金が支払われているのか?不明な点は多いですが、testerなんて誰がやっても似たようなものなのに、なんでこんな仕事の回数の差でレーティングと給料の相関が出てくるんだ(多分出てくると思う...)って話になると思います。日本代表とsnukeしかいないJOIのチューターだって、合宿の4位と5位に何の違いがあるんだと言われたらそれまでだと思います。それに僕が400万ウォンを手にしたSCPCだって、World Finalに行かなかったら知りえなかったコンテストですし。*2

あとそれから最近のTwitterの鍵垢の風潮は何なんですか。

オンサイトのたびに大暴れしているレッドコーダーたちが映画に出てくるようなステレオタイプなdumb jocksみたいだという話

あの手の映画の中ではまだマシなのでThunderstruckを見ましょう。あの風潮が改善するのは今の人たちが全員引退しても無理じゃないかな......

Youtubeの動画を作ることの大変さ

唐突なように見えて前にもブログに書いたことがありますが、競プロ飛沫テクを紹介するSothe the Algorithm Wolfの内臓です。
まあこれを始めたきっかけは他でもなく英語を話す練習で、まあx1.5で聞いてもらえればいい感じに聞こえるのですが、これ無駄に準備が大変でした。

楽な動画の例としては、何も準備しなくてできるコンテストサイトとかツールの紹介動画です。多分参加記ビデオとかを作るとしてもこの類になります。これを作るには、ネタと声を出して録音できる環境(これが難しい!)があれば良いです。
問題はテクニック紹介動画です、これは確かに視聴者を増やすのにはいいのですが、準備が大変すぎます。スライドを作ったりしないといけないので、本来の趣旨からは完全に外れてしまって非効率的すぎます(interview questionsの練習にはなるかも?)。とはいえ前述のコネが作れるという可能性もあるのかもしれません(Code Festivalに参加していたFatalEagleさんは僕のチャンネルの存在を知ってくれてましたし)。

とにかくこういうのは非効率なので本当に時間がある時にやるべきです。

全体を通して何が伝えたかったか?

いや別に何も......

*1:http://japlj.hatenablog.com/entry/2015/12/01/000210

*2:いろいろ言ってますが僕はこれの恩恵受けてるのでこれからもこんなクソみたいな状況であってほしいし、他の人もどんどんこのクソ温室を目指して精進していってほしい

IIDX DPの攻略オプションを眺める

この記事は #音ゲーマー達の発信所 (1枚目) Advent Calendar 2016 - Adventar の5日目

ハードクリアした曲のうちオプションが独特と感じたものを書き並べていきます。

Do it!! Do it!! [DPA] R乱 / R乱

ここに癖がつきます。

この曲はこの後の発狂は白い縦連打だったり白いトリルだったりするので、R乱で外れになるケースがそんなにありません。純粋に癖だけない状態で正規譜面に取り組める点がすばらしい。

eRAseRmOToRpHAntOM [DPA] FLIP R乱 / -

この曲みたいに軸が目立つ系は1軸が真ん中によるので、左鏡が安定だと思います。しかしこの曲の左寄り傾向はかなり顕著です。
具体的にはここ

これが異様に押しにくいので、R乱でうまいこと崩してやったほうが押しやすくなりました。無理皿は当然出ますがまあ時々1軸が3とかにも来てくれるので頑張ってください……

Snake Stick [DPA] - / 鏡

前提として覚えておくべき皿は、前半の発狂まで全部と、その後の24分の位置だと思います。残りは適当に回せばいいと思います(逆サイドのほうが注目すべきではある)。前半をちゃんと覚えておかないといけない理由は、逆サイドの鍵盤がいやらしいことが多々あることです。こういうのとか
オプションを決めた最大の理由は左鏡で癖がついてどうしようもなくなったことですが、ここが内よりになるところが評価できます。

VANESSA [DPA] R乱 / R乱

全体的に意味不明な美しさがあるため正規が押しにくいですが、やはり美しいだけあってランダムだと普通に難しくなります。解決策として出てきたのが両R乱で、一番無難な配置が生まれます。
元から着地や無理皿が結構厳しい置かれ方をしているので崩した時のデメリットが比較的小さいことも挙げられます。

アストライアの双皿 [DPA] 鏡 / 鏡

一番理解不能なオプション選択だと思います。もちろんこの曲はここを100%から始めて2%以上残すだけのゲームです。
こんな配置どう見ても大ハズレのように見えるんですが予想外にゲージが残るのでここでゲージが総崩れする人は騙されたと思って一度やってみるべきだと思います。

Real [DPA] FLIP - / R乱

今後こういう譜面が出てくるのか知りませんが、こういった真っ白発狂はR乱で外れにくい上見やすくなるのでR乱がかなり有力な選択肢になると思います。

Innocent Walls [DPH] S乱 / S乱

これはDPAでは使えないと思います。
こんな調子の譜面がひたすら続くので、崩したほうがよっぽどやりやすいです。
二重発狂が時々出てくるので適正レベルでは厳しいのですが、Innocent Walls[DPH]は☆11全白した後でも難しかったので、そういう地力がある人向けのオプションです。
一方DPAはSPHの縦連地帯の餡蜜が簡単です。

まとめ

これはR乱の普及記事だったのでは?
他にR乱が良さそうなものにはQUANTUM TELEPORTATION[DPA]もあるらしいんですがわかんないよそんなの。

Google 翻訳

試しに使ってみる。

競技プログラミングの解説のような文章

AGC007のEの解説みたいなのを日本語で書いてみたので、これを英語に翻訳してみる。

まずは答えで二分探索。この判定においては、データ構造をマージする一般テクなテクを使う。それぞれの再帰の中では、小さいほうのvectorのそれぞれの要素に対し、もう片方から出てくるときに最小どのくらいの深さにできるかを、大きいほうのvectorにおいて二分探索を使うことによって求めることができる。最初に答えで二分探索をするのも合わせて、時間計算量はO(N log^2 N log ans)。

Firstly, we will search for bisection by answer. In this judgment, we use a generic technique to merge data structures. Within each recursion it can be found that for each element of the smaller vector you can determine how far it can be at the minimum when coming out from the other by using a binary search in the larger vector it can. The time complexity is O (N log ^ 2 N log ans) together with the first binary search by answer.

お上手

bemaniwikiの特殊詐称・個人差譜面リストみたいな文章

なかなか個人差も大きそうでハードも難しそうなアストライアの双皿[DPA]について何もかかれてないので、これを日本語で書いて英語に翻訳してみる。

中盤、曲名の通り両サイドに連皿とそれに付随する鍵盤が降ってくるという、他の譜面では見ないような配置が降ってくる。その後回復が続くためノマゲは☆12中位であるが、ハードはこの地帯が上手く出来るかに完全に左右される。また、簡単ではあるが繰り返し配置が多いので、そこで癖がつかないように注意。

In the midfield, as the song title says, the dishes and accompanying keyboards fall down on both sides, the arrangement which will not be seen on other music notes will come down. Since recovery then continues, the magma is ☆ 12 medium, but hard depends entirely on how well this area can be done. Also, although it is simple, there are many repetition arrangements, so be careful not to get into your habit there.

dishes

magmaはどこから出てきたのか……

CFに書いた英語の記事

Youtube channel: Tips for programming contestsから。これを日本語に翻訳してみる。

In this channel, I'll introduce many little tips that is not on textbooks but useful for problem solving. I've solved 4000+ programming problems overall and I got a lot of practical knowledge from that, so I want to explain various kind of educational approaches from my experiences. I'm not explaining how typical ones (such as segment trees or flow algorithms) work because a lot of better videos are already on YouTube, and I'm focusing on tricky techniques (and I'm sure that many redcoders know about these techniques). I think that most of those can be applied for only specific several problems, but those tips are very important to get higher places.

このチャンネルでは、教科書ではなく問題解決に役立つ多くのヒントを紹介します。 私は4000以上のプログラミング問題を全面的に解決しており、実際の知識はたくさんあるので、私の経験からさまざまな教育方法を説明したいと思います。 どのように典型的なもの(セグメントツリーやフローアルゴリズムなど)がうまくいくのかについては説明していません。なぜなら、より多くの優れたビデオが既にYouTube上にあるからです。トリッキーなテクニックに焦点を当てています(多くのredcodersがこれらのテクニック )。 私はそれらのほとんどが特定のいくつかの問題だけに適用できると思うが、それらのヒントはより高い場所を得るために非常に重要である。

うん、すごく意味通ってますね。文体とかが不自然(主語をまじめに訳すので)なんだけどそれはもうどうしようもないかな。

コピペ改変

適当に元から存在する文章置き換えただけのものですが、これを英語に翻訳してみる。

地力は絶対に裏切らない
運指は何処にも行かない
日本でも海外でもEDENはEDEN
リアランプは盗まれない
資産と違いBPの価値は不変
努力が100%実る
これらの普遍的事実がDBR教を信仰する人々に絶対的な安心を与え精神を安定させる。DBRこそ正義。DBRこそ真実。大好き♡

Absolutely not betray ground
Fingering does not go anywhere
EDEN in Japan and overseas EDEN
Clear lamps can not be stolen
Unlike assets BP's value is immutable
Effort is 100% fruit
These universal facts give absolute relief to those who believe in DBR teaching and stabilize the spirit. DBR is justice. DBR is the truth. I love you

Effort is 100% fruit

DP fruitさんの強さの秘訣…?

文章が短くて曖昧だとまあ翻訳難しいですよね。

固有名詞が多すぎる文章

地名についてよくつぶやいたりするので、これが上手くいくかを試してみます。

千葉県いすみ市は、2005年に大原町、夷隅町、岬町が合併して出来た市である。房総半島南東部に位置しており、詳細な位置を知りたい場合は、市章を見れば分かるだろう(嘘だと思う人は、本当に市章を見ることをおすすめする)。いすみ市の人口は4万人弱であり、勝浦市一宮町睦沢町大多喜町御宿町と隣接している。(合併のせいか、)姉妹都市や友好都市関係は、特徴あるものが多い。中でもいすみ市(旧大原町)とミネソタ州ダルース市の姉妹都市関係は、偶然のような歴史的なつながりをもっており、興味深い。余談だがダルース市は極寒の地にあるので、他の姉妹都市も極寒の地である。中でもロシア連邦カレリア共和国のペトロザヴォーツク市に関しては、知らない者はいないであろう。

Isuimi City, Chiba Prefecture Isuimi City is a city that was formed by the merger of Ohara Town, Izumi Town and Misaki Town in 2005. It is located in the southeastern part of the Boso Peninsula, and if you want to know the detailed position, you will know by seeing the city chapter (people who think it is a lie, really recommend seeing the city chapters). The population of Isumi City is nearly 40,000, which is adjacent to Katsuura City, Ichinomiya Town, Mutsuzawa Town, Otaki Town and Onjuku Town. (Due to the merger) many sister cities and friendship city relations are characteristic. Among them, the sister city relationship between Isumi City (Old town Ohara) and Duluth City Minnesota is interesting, as it has a historical connection like a coincidence. Aside from that, since Duluth City is in an extremely cold place, other sister cities are also extremely cold. Among them, no one in the Russian Federation Karelia Republic of Petrozavodsk has no idea.

残念ながら地名が正しく読めていません。中盤は一般的な文章の香りがするのか、すごく上手く訳されていますね。

まとめ

一般的な文章はかなり上手く訳されるので一般的な文章を英語にしたいときに使いましょう。和訳はこれなくても読めるし不自然な日本語ではあるので、僕は使わないと思います。

KUPC 2016

解ける問題がなくて暇なのでコンテスト中ですがブログを書いています。

A - バリケード

呼吸。

#include<stdio.h>
#include<algorithm>
using namespace std;
int main(){
	int a,b,c;scanf("%d%d%d",&a,&b,&c);
	int ret=0;
	for(int i=0;i<a;i++){
			int p;scanf("%d",&p);
			if(p<b||p>=c)ret++;
	}printf("%d\n",ret);
}

B - 作問委員会

呼吸。

#include<stdio.h>
#include<algorithm>
using namespace std;
int main(){
	int a,b,c;scanf("%d%d%d",&a,&b,&c);
	int ret=0;
	for(int i=0;i<a;i++){
			int p;scanf("%d",&p);
			if(p<b||p>=c)ret++;
	}printf("%d\n",ret);
}

C - クッキー☆増殖装置

どうせこうでしょ

#include<stdio.h>
#include<algorithm>
using namespace std;
int main(){
	int T;scanf("%d",&T);
	while(T--){
		int a,b;scanf("%d%d",&a,&b);
		int ret=127*(a-1);
		if(a%2==0)ret+=127-b;
		else ret+=b;
		printf("%d\n",ret);
	}
}

D - 長い黒板

一列ずつ増やしていく。

#include<stdio.h>
#include<algorithm>
using namespace std;
char str[2][200];
char in[3];
int main(){
	int a;scanf("%d",&a);
	int dir=0;
	for(int i=0;i<a;i++){
		if(dir==0){
			str[0][i+1]=str[1][i+1]=0;
			bool ok=false;
			for(int j=0;j<4;j++){
				for(int k=0;k<2;k++){
					if(j&(1<<k))str[k][i]='#';
					else str[k][i]='.';
				}
				printf("%s\n",str[0]);
				printf("%s\n",str[1]);
				fflush(stdout);
				scanf("%s",in);
				if(in[0]=='T'){
					ok=true;
					break;
				}
			}
			if(!ok)dir=1;
		}

		if(dir){
			for(int j=i-1;j>=0;j--){
				str[0][j+1]=str[0][j];
				str[1][j+1]=str[1][j];
			}
			str[0][i+1]=str[1][i+1]=0;
			for(int j=0;j<4;j++){
				for(int k=0;k<2;k++){
					if(j&(1<<k))str[k][0]='#';
					else str[k][0]='.';
				}
				printf("%s\n",str[0]);
				printf("%s\n",str[1]);
				fflush(stdout);
				scanf("%s",in);
				if(in[0]=='T'){
				//	ok=true;
					break;
				}
			}
		}
	}
	printf("%s\n%s\n",str[0],str[1]);

}

E - 柵

定番の思考放棄。

#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int D_MAX_V=20002;
const int D_v_size=20002;
struct D_wolf{
	int t,c,r;
	D_wolf(){t=c=r=0;}
	D_wolf(int t1,int c1,int r1){
		t=t1;c=c1;r=r1;
	}
};
vector<D_wolf>D_G[D_MAX_V];
int D_level[D_MAX_V];
int D_iter[D_MAX_V];

void add_edge(int from,int to,int cap){
	D_G[from].push_back(D_wolf(to,cap,D_G[to].size()));
	D_G[to].push_back(D_wolf(from,0,D_G[from].size()-1));
}
void D_bfs(int s){
	for(int i=0;i<D_v_size;i++)D_level[i]=-1;
	queue<int> Q;
	D_level[s]=0;
	Q.push(s);
	while(Q.size()){
		int v=Q.front();
		Q.pop();
		for(int i=0;i<D_G[v].size();i++){
			if(D_G[v][i].c>0&&D_level[D_G[v][i].t]<0){
				D_level[D_G[v][i].t]=D_level[v]+1;
				Q.push(D_G[v][i].t);
			}
		}
	}
}
int D_dfs(int v,int t,int f){
	if(v==t)return f;
	for(;D_iter[v]<D_G[v].size();D_iter[v]++){
		int i=D_iter[v];
		if(D_G[v][i].c>0&&D_level[v]<D_level[D_G[v][i].t]){
			int d=D_dfs(D_G[v][i].t,t,min(f,D_G[v][i].c));
			if(d>0){
				D_G[v][i].c-=d;
				D_G[D_G[v][i].t][D_G[v][i].r].c+=d;
				return d;
			}
		}
	}
	return 0;
}
int max_flow(int s,int t){
	int flow=0;
	for(;;){
		D_bfs(s);
		if(D_level[t]<0)return flow;
		for(int i=0;i<D_v_size;i++)D_iter[i]=0;
		int f;
		while((f=D_dfs(s,t,99999999))>0){flow+=f;}
	}
	return 0;
}
char str[200][200];
int main(){
	int a,b;scanf("%d%d",&a,&b);
	for(int i=0;i<a;i++)scanf("%s",str[i]);
	int T=9999;
	int B=-1000;
	int L=9999;
	int R=-9999;
	for(int i=0;i<a;i++)for(int j=0;j<b;j++){
		if(str[i][j]=='X'){
			T=min(T,i);
			B=max(B,i);
			L=min(L,j);
			R=max(R,j);
		}
	}
	if(T==0||B==a-1||L==0||R==b-1){
		printf("-1\n");return 0;
	}
	int s=2*a*b;
	int t=2*a*b+1;
	for(int i=0;i<a;i++)for(int j=0;j<b;j++){
		if(str[i][j]=='X')add_edge(s,(i*b+j)*2+1,99999999);
		else add_edge((i*b+j)*2,(i*b+j)*2+1,1);
		if(i)add_edge((i*b+j)*2+1,(i*b+j-b)*2,99999999);
		else add_edge((i*b+j)*2+1,t,99999999);
		if(i<a-1)add_edge((i*b+j)*2+1,(i*b+j+b)*2,99999999);
		else add_edge((i*b+j)*2+1,t,99999999);
		if(j)add_edge((i*b+j)*2+1,(i*b+j-1)*2,99999999);
		else add_edge((i*b+j)*2+1,t,99999999);
		if(j<b-1)add_edge((i*b+j)*2+1,(i*b+j+1)*2,99999999);
		else add_edge((i*b+j)*2+1,t,99999999);
		
	}
	printf("%d\n",max_flow(s,t));
}

F - 早解き

慎重に条件をいろいろ持ちながら前から構文解析していく。
慎重さが難しいですね〜。

#include<stdio.h>
#include<algorithm>
using namespace std;
char in[1100];
int at=0;
pair<int,int>expr(int a,int b);
pair<int,int>term(int a,int b){
	if('0'<=in[at]&&in[at]<='9'){
		if(in[at]=='0'){
			at++;
			return make_pair(0,at-1);
		}
		pair<int,int>ret=make_pair(-2,0);
		if(in[at]-'0'>b){
			ret=make_pair(b+1,at);
		}
		if((a>(in[at]-'0')*10+9)){
			ret=make_pair(a-1,at);
		}
		int v=0;
		while('0'<=in[at]&&in[at]<='9'){
			v*=10;v+=in[at]-'0';
			at++;
		}
		if(ret.first==-2){
			if(v>=10)ret=make_pair(v,at-1);
			else ret=make_pair(v,at);
		}
		return ret;
	}else{
		return expr(a,b);
	}
}
pair<int,int>expr(int a,int b){
	//printf("%d %d %d\n",at,a,b);
	if(in[at]!='^'&&in[at]!='_'){
		return term(0,99);
	}
	int z=0;
	if(in[at]=='^'){
		z=1;
	}
	at+=2;
	pair<int,int> tmp=term(a,b);
	pair<int,int>ret=make_pair(-2,-2);
	if(z==0&&tmp.first==0)ret=tmp;
	if(z==1&&tmp.first==99)ret=tmp;
	if(tmp.first!=-1&&z==0&&a>tmp.first)ret=tmp;
	if(tmp.first!=-1&&z==1&&b<tmp.first)ret=tmp;
	at++;
	int L=a;int R=b;
	if(z==0){if(tmp.first!=-1)R=min(R,tmp.first-1);}
	else {if(tmp.first!=-1)L=max(L,tmp.first+1);}
	pair<int,int> tmp2=term(L,R);

	if(tmp.first==-1&&z==0)tmp.first=110;
	if(tmp2.first==-1&&z==0)tmp2.first=110;
	if(ret.first==-2){
		if(z==0)ret.first=min(tmp.first,tmp2.first);
		else ret.first=max(tmp.first,tmp2.first);
		ret.second=tmp2.second;
	}
	at++;
	return ret;
}
int main(){
	int a;scanf("%d",&a);
	while(a--){
		scanf("%s",in);
		at=0;
		pair<int,int> ret=expr(0,99);
		printf("%d %d\n",ret.first,ret.second+1);
	}
}

G - 試験

Dilworthから察するにどうせ同じ長さの部分文字列の種類数の最大を答えればいいと思う
最近のコンパイラだと "rank" で落ちる?

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
char c[110000];
int n;
int sa_k;
int Rank[110000];
int tmp[110000];
int sa[110000];
int lcp[110000];
bool compare_sa(int i,int j){
	if(Rank[i]!=Rank[j])return Rank[i]<Rank[j];
	else{
		int ri=i+sa_k<=n?Rank[i+sa_k]:-1;
		int rj=j+sa_k<=n?Rank[j+sa_k]:-1;
		return ri<rj;
	}
}
void construct_sa(){
	for(int i=0;i<=n;i++){
		sa[i]=i;
		Rank[i]=i<n?c[i]:-1;
	}
	for(sa_k=1;sa_k<=n;sa_k*=2){
		sort(sa,sa+n+1,compare_sa);
		tmp[sa[0]]=0;
		for(int i=1;i<=n;i++){
			tmp[sa[i]]=tmp[sa[i-1]]+(compare_sa(sa[i-1],sa[i])?1:0);
		}
		for(int i=0;i<=n;i++){
			Rank[i]=tmp[i];
		}
	}
}

void construct_lcp(){
	for(int i=0;i<=n;i++)Rank[sa[i]]=i;
	int h=0;
	lcp[0]=0;
	for(int i=0;i<n;i++){
		int j=sa[Rank[i]-1];
		if(h>0)h--;
		for(;j+h<n&&i+h<n;h++){
			if(c[j+h]!=c[i+h])break;
		}
		lcp[Rank[i]-1]=h;
	}
}
int hd[110000];
int main(){
	scanf("%s",c);
	n=strlen(c);
	construct_sa();
	construct_lcp();
	int ret=0;
	for(int i=0;i<n;i++)hd[lcp[i]]++;
	int cnt=0;
	for(int i=n;i>0;i--){
		cnt++;
		cnt-=hd[i];
		ret=max(ret,cnt);
	}
	printf("%d\n",ret);
}

H - 壁壁壁壁壁壁壁

60点までは問題文にある通りにMCF。満点はどうせMCFのよくあるアルゴリズムの代わりにsegtreeで高速に見ていくんだと思う。

I - ティッシュ配り

Cの値はほぼ意味がないので、1だと思うことにすると、人iが残りj時間で何人に増え、合計いくつ処理できるか、みたいな感じのものを求めていくことになる。 人iの種類は高々 O(√N)。
10^7.5 定数倍改善。普通に後ろからDPしていくだけ。全部のテーブルを持っておくとTLEするし、直前だけ持ってても情報が足りないが、そこはまあ人iについてはi個前まで持っておくとかすればメモリ使用量は合計 O(N)になるわけで。

速度が足りないので途中から450個全部見ないようにして920ms/1000ms で無理やり通した。さすがに想定解ではないと思う。

#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;
long long mod=1000000007;
int N[110000];
int C[110000];
pair<int,int>p[110000];
long long ans[110000];
vector<int> dp1[450];
vector<int> dp2[450];
int main(){
	int a;scanf("%d",&a);
	for(int i=0;i<a;i++){
		scanf("%d%d",N+i,C+i);
		p[i]=make_pair(N[i]/C[i],i);
	}
	std::sort(p,p+a);
	for(int i=0;i<450;i++){
		dp1[i]=vector<int>(i+1,0);
		dp2[i]=vector<int>(i+1,0);
		dp2[i][0]=1;
	}
	int at=0;

	for(int i=1;i<100010;i++){
		int M=450;
		if(i>50000)M=330;
		for(int j=0;j<M;j++){
			int tk=2*(j+1);
			int v=i%(j+1);
			if(j<449&&tk<=i){
				dp1[j][v]=(dp1[j][v]+dp1[j+1][(i-tk/2)%(j+2)]);
				if(dp1[j][v]>=mod)dp1[j][v]-=mod;
				dp2[j][v]=(dp2[j][v]+dp2[j+1][(i-tk/2)%(j+2)]);
				if(dp2[j][v]>=mod)dp2[j][v]-=mod;
			}else{
				dp1[j][v]=i;
				dp2[j][v]=1;
			}
		}
		while(at<a&&p[at].first==0)at++;
		while(at<a&&p[at].first==i){
		//	printf("%d: %lld %lld\n",p[at].second,dp1[0][0],dp2[0][0]);
			ans[p[at].second]=((long long)C[p[at].second]*dp1[0][0]+(long long)(N[p[at].second]%C[p[at].second])*dp2[0][0])%mod;
			at++;
		}
	}
	for(int i=0;i<a;i++)if(N[i]/C[i]==0)ans[i]=N[i];
	for(int i=0;i<a;i++)printf("%lld\n",ans[i]);
}

まとめ

典型実装重視型には解きやすいセット。

ARC053 D: 2 つの山札

こういう後から合流して重複しうるものをうまく重複しないような汎用的テクとして、1ステップ進むときに追加する文字が同じなのに到達先が違う、というケースがないように、同じ文字で複数通りの結果に分岐するときはそれらを集合として状態に持っておく、というものがある。

多分このDPでもちゃんと遷移順とかしっかりやって普通のDPテーブルに押し込むことは可能で、そうするとO(N^2)になるんだろうけど、ややこしいのでサボった。

見た感じもっと実装が楽な解があるっぽいけど、発想を無にする戦略でいくならこっちの方が楽。

#include<stdio.h>
#include<algorithm>
#include<map>
#include<queue>
using namespace std;
int p[1100];
int q[1100];
long long mod=1000000007;
struct state{
	short t[4];
	state(){for(int i=0;i<4;i++)t[i]=-1;}
};
inline bool operator<(const state &a,const state &b){
	for(int i=0;i<4;i++)if(a.t[i]!=b.t[i])return a.t[i]<b.t[i];
	return false;
}
int pn[1100];
int qn[1100];
int main(){
	int a;scanf("%d",&a);
	for(int i=0;i<a;i++){scanf("%d",p+i);p[i]--;}
	for(int i=0;i<a;i++){scanf("%d",q+i);q[i]--;}
	reverse(p,p+a);
	reverse(q,q+a);
	for(int i=0;i<a;i++)pn[i]=qn[i]=-1;
	for(int i=a-1;i>0;i--){
		pn[p[i-1]]=p[i];
		qn[q[i-1]]=q[i];
	}
	queue<state>Q;
	map<state,int>dp;
	state st;
	st.t[0]=p[0];
	st.t[1]=q[0];
	Q.push(st);
	dp[st]=1;
		
	while(Q.size()){
		state now=Q.front();
		Q.pop();
		int tmp=dp[now];
	//	printf("%d %d %d %d: %d\n",now.t[0],now.t[1],now.t[2],now.t[3],tmp);
		for(int i=0;i<4;i++){
			if(now.t[i]==-1)continue;
			bool ok=true;
			for(int j=0;j<i;j++)if(now.t[i]==now.t[j])ok=false;
			if(!ok)continue;
			state to;
			int ind=0;
			for(int j=0;j<4;j++){
				if(now.t[j]==now.t[i]){
					int ad;
					if(j%2==0)ad=qn[now.t[j^1]];
					else ad=pn[now.t[j^1]];
				//	printf("%d %d\n",now.t[j^1],ad);
					if(ad!=-1){
						to.t[ind+j%2]=now.t[j];
						to.t[ind+!(j%2)]=ad;
						ind+=2;
					}
				}
			}
		//	printf("%d %d %d %d\n",to.t[0],to.t[1],to.t[2],to.t[3]);
			if(to.t[0]<to.t[2]){
				swap(to.t[0],to.t[2]);
				swap(to.t[1],to.t[3]);
			}
			if(to.t[0]==to.t[2]&&to.t[1]==to.t[3]){
				to.t[2]=to.t[3]=-1;
			}
			if(dp.count(to))dp[to]=(dp[to]+tmp)%mod;
			else{
				dp[to]=tmp;
				Q.push(to);
			}
		}
		if(~pn[now.t[0]]||~qn[now.t[1]])dp.erase(now);
	}
	state go;
	go.t[0]=p[a-1];
	go.t[1]=q[a-1];
	printf("%d\n",dp[go]);
}

SCPC2016 本選

Sorry for foreign coders, I'm writing this blog in Japanese. ;(

8/18 に 第2回삼성대학생프로그래밍경진대회という大会があったので、観光も兼ねて行ってきました。韓国に行くのは初めてです。

8/16 出発日
また成田空港に行きます。今回は交通費が出ないのでアクセス特急です。
f:id:tozangezan:20160816114613j:plain
悪天候で飛行機の到着が遅れたので出発も遅れました

f:id:tozangezan:20160816134944j:plain
さすがにこの「いわき」は間違ってる気がする

f:id:tozangezan:20160816162408j:plain
仁川国際空港から、空港鉄道の時刻表。成田よりは便利?

f:id:tozangezan:20160816164704j:plain
海だか川だかをよく渡る

f:id:tozangezan:20160816181407j:plain
東大門の近くのホテルに着きました。

f:id:tozangezan:20160816185215j:plain
f:id:tozangezan:20160816185227j:plain
f:id:tozangezan:20160816185538j:plain
この日はかの有名な東大入口駅を観光(近い)

f:id:tozangezan:20160816190615j:plain
東大入口駅近くの小さな食堂で夕食。
ゴマ入り豆腐鍋?おかずもやたらと多いし鍋そのものの味があんまりないので、キムチとかを沢山入れたりしたが、正しい食べ方なのかはよくわからん。

f:id:tozangezan:20160816210920j:plain
夜はこんな感じ

8/17 観光
この日はkcmさんと各地を観光していました。

f:id:tozangezan:20160817100709j:plain
光化門駅、大きな書店があるのでそこに行くことにした

f:id:tozangezan:20160817111801j:plain
「人は本を作り、本は人を作る」

f:id:tozangezan:20160817134346j:plain
昼食は冷麺、kcmさんのアイコンに使われるほどのお店です。うん、おいしい。

f:id:tozangezan:20160817141033j:plain
平壌 乙密台」日本でも記事になる程度には超有名店らしい。

午後はソウル大学に行きました

f:id:tozangezan:20160817145500j:plain
ソウル大入口駅。大学は山の中にある(筑波大学のイメージ?)のでバスに乗り継ぎだけど、乗り継ぎは割引があって実質無料レベルで乗れます。

f:id:tozangezan:20160817154129j:plain
東大で言う「理学部7号館地下」的な部屋の一角に、競技プログラマーの空間がある。

f:id:tozangezan:20160817164118j:plain
f:id:tozangezan:20160817164228j:plain
東京とソウルの最も大きな違いがこの(数百メートル級)山の多さだと思った

f:id:tozangezan:20160817172928j:plain
見たことがない青い鳥がいたのですが、これはカササギと言って(カラスの仲間)韓国には沢山いるそうです。
どこにいるのか分からない人は、カササギ - Wikipediaの写真にあるように白い腹をさがせば分かると思う

f:id:tozangezan:20160817174210j:plain
f:id:tozangezan:20160817174206j:plain
どこの国に行っても、大学食堂は大学食堂。

8/18 コンテスト当日
コンテスト会場は市内の辺境にあって、市外からバスに乗り継ぐような感じ。
f:id:tozangezan:20160818103120j:plain
f:id:tozangezan:20160818103235j:plain
ソンバウィ駅。地図の通り駅周辺に特に何もない。

f:id:tozangezan:20160818104411j:plain
f:id:tozangezan:20160818104422j:plain
会場周辺。
6年前はこんな感じだったらしい。
네이버 지도

最初の練習早解きコンテスト(問題文英語)で1位を取ったので
www.samsung.com
を貰った。

本番コンテスト(4時間4問)
OI系の問題のたいへん枠を集めたようなセット。問題文訂正でまたリジャッジあったがそこでペナルティ消せてずるい気分になった(?)
明らかに作戦ミスだったけど8位。
特に3*3スタンプを細長い場所に押すときの状態数は少ない、みたいな問題が出たが、それは既出らしい?
問題文がやたらと長くて読むのが大変な問題もあったが、無事大きい部分点が取れた。

「n等」に複数人いる関係で、8位だが3等という賞になった。
f:id:tozangezan:20160818184111j:plain
millionaire?
他にもらったものは、(コンテストで使った)巨大なマウスパッド、(コンテストで使った)メカニカルキーボード、いつものコンテストで貰える諸々。

夜は他のコンテスタントたちと焼肉を食べに行った。(セマウル食堂という、日本にもいくつかある有名チェーン店らしい)
f:id:tozangezan:20160818201546j:plain
夜の町並み

f:id:tozangezan:20160818204807j:plain
f:id:tozangezan:20160818210125j:plain
f:id:tozangezan:20160818202848j:plain
日本の一部の競技プログラマーの間での有名フレーズ「他人のお金で焼肉が食べたい」を実現してしまった。(잘 먹었습니다!)

8/19 帰国日
あんまり時間もないしそれほどちゃんとは観光できないので、ソウル駅を見に行くことにする。
ソウル駅の近くには軍人みたいな人がたくさんいた。

f:id:tozangezan:20160819111815j:plain
韓国ではロッテグループが強いのでロッテリアが結構たくさんある。これはソウル駅横にある百貨店的なものの中のフードコート。
小さめのセットだったが2900ウォンとかで安かった。

f:id:tozangezan:20160819113030j:plain
KORAILの建物。

f:id:tozangezan:20160819113543j:plain
f:id:tozangezan:20160819114251j:plain
駅舎とホーム。

f:id:tozangezan:20160819113735j:plain
古い駅舎、戦前の日本で流行ってそう(cf: 東京駅)なデザインなので、日本統治時代の建物だと思う。

f:id:tozangezan:20160819130213j:plain
金浦空港はかなり小さい空港でした。が、何かやってました。

帰りは羽田行き。かなり近い。

f:id:tozangezan:20160819181125j:plain
何度もお世話になった多摩川河口干潟


まとめ:
総じて(特に金銭面で!)すばらしいコンテストだったので韓国語読める人は是非参加しましょう!
(だけどコンテスト会場ではずっと(インタビューですら)韓国語だしあんまり話したり聞いたりできないので大変だった…)

Distributed Code Jam 2016 Finals

memo: FHC 2015 Finals の参加記的なのも書いてないらしい。さすがに昔すぎて今から書く気になれない。

今年のGCJDCJのFinal roundはGoogle New Yorkで開催されました。

8/3 出発
f:id:tozangezan:20160803090430j:plain
成田空港にはなんかいろいろ店が出来ている

f:id:tozangezan:20160804024904j:plain
ホテルから見える光景。この時点で日本では4日になっている。

f:id:tozangezan:20160803174120j:plain
夕食(一風堂)(あのさぁ…) 味は日本のと同じ。
こういう移動日には$30、移動でない日には$60が食費として請求できるので、贅沢ができる。

8/4
まあいろいろあって1日早く出発していて、他のコンテスタントは今日が移動日でした。
f:id:tozangezan:20160804093233j:plain
朝食。こんな感じの何も入っていないベーグルがやたらと多くて食べていて飽きる。

f:id:tozangezan:20160804123741j:plain
川の向こうはニュージャージー州です。

f:id:tozangezan:20160804150854j:plain
こんな感じで部屋で起きているときは基本的にESPNつけっぱなしです。

この日飲み物でも買おうと思ってホテル近所のスーパーに言ったらよすぽとりんごさんがいたので合流した。
あとは適当に遊んで寝るなどした。

8/5 GCJ 本番
GCJの日はDCJコンテスタントは暇なので、会場に行って観戦をしていてもいいし、適当に観光していてもOKでした。
後者を選びました。wafrelkaと連絡がなかなかつかなかった。

街中を走るバスのチケットを貰ったので乗っていたが異様なほどに遅かったのでそれだけで2時間以上つぶせた。
f:id:tozangezan:20160805135624j:plain
f:id:tozangezan:20160805143623j:plain
左が州の旗で、右が市の旗です。
f:id:tozangezan:20160805155143j:plain
有名な緑道です。

f:id:tozangezan:20160805182254j:plain
f:id:tozangezan:20160805182607j:plain
アメリカなのでこんな感じで鷲をロゴにしたものがいっぱい見つかります。ちなみにIOI2015会場のカザフスタンの大学の食堂でもイヌワシの写真が壁紙に使われていました。

f:id:tozangezan:20160805194041j:plain
GCJコンテストはパーティーでボウリングをした。

8/6 DCJコンテスト当日

f:id:tozangezan:20160806094106j:plain
こんな感じに通りに番号がついています。

f:id:tozangezan:20160806095001j:plain
Google New York オフィス
f:id:tozangezan:20160806120540j:plain

コンテストは自明を早解きしたらできることがなくなって適当に嘘解法を実装するくらいしかできませんでした。

f:id:tozangezan:20160806174716j:plain
諦めて計算用紙で遊んでいた。

f:id:tozangezan:20160806175601j:plain

この日の夕食は高級そうな場所でパーティーだったんですが食べ物全然なくて困りました。部屋に帰ると12個いりベーグル小がありこれもつらい気持ちになります。

8/7 帰る日

f:id:tozangezan:20160807064522j:plain
タクシーは(空いてると)速い。

f:id:tozangezan:20160807072603j:plain
翌週から韓国に行くというのに空港のフードコートで韓国料理を食べる

8/8 到着日
飛行機乗ってる間はずっと昼間なのですが、日付変更線の関係でつく頃には翌日になっています。

f:id:tozangezan:20160808132622j:plain
写真はチバラギ共和国との国境です。(たぶん)

帰ったら即reimbursementの諸々を送って旅行終了。
いろいろ貰ったけど翌週の大会でやたらとプレゼント等を貰いすぎて印象が薄れています。
DCJでは例えば、靴を貰いました。