Hatena::Grouptopcoder

kojingharangの日記 RSSフィード

|

2014-07-05

2014 TCO Round2C 300 SubstringReversal

05:59 |  2014 TCO Round2C 300 SubstringReversal - kojingharangの日記 を含むブックマーク はてなブックマーク -  2014 TCO Round2C 300 SubstringReversal - kojingharangの日記  2014 TCO Round2C 300 SubstringReversal - kojingharangの日記 のブックマークコメント

  • 与えられた長さNの文字列の連続部分文字列 [s,e], (s, e in [0, N) ) 1コを反転させる。どこを反転させると辞書順最小になるか。
  • 解が複数あるときは s*10000+e が最小のものを返す.
  • 1≦|文字列|≦2500
  • 全探索だと N^3 でだめ
  • 真ん中くらいに a があるときは最初から a の位置まで反転させればだいたい最小
  • abdc は dc を反転させないといけない。これはどうする?
  • たぶん ab の部分はすでに解(辞書順最小)の一部だから反転させなくてよいのだ
  • 仮説→反転の始点は、文字列をソートしたものと文字列が一致しない最初の位置 B であるとしていい
  • (証明)Bより左のB'では、B'の文字より小さい文字は右側にないので反転してもいいことない。
  • Bより右側のB'ではBより小さい文字が右側にあるのにBとその文字を交換することができない。なのでBじゃないとだめ
  • というわけで B〜E in [B, N) を反転した文字列を全て列挙して (文字列, E) が最小のものの B, E を返せばいい
  • Accepted
  • 写経 challenge 1 コ成功.
  • 証明済みのコードを提出したので、結果だけじゃなく質的にも良かったと思う。
class SubstringReversal {
	public:
	vector <int> solve(string S) {
		vector<int>ans(2);
		int N=S.size();
		string ref=S;
		sort(ALL(ref));
		if(S==ref) {
			return ans;
		}
		int B=N-1;
		REP(i, N) if(S[i]!=ref[i]) {B=i;break;}
		ans[0]=ans[1]=B;
		if(B<N-1) {
			vector<pair<string, int> > w;
			RANGE(i, B, N) {
				string s = S.substr(B, i-B+1);
				reverse(ALL(s));
				w.PB(MP(s+S.substr(i+1), i));
			}
			sort(ALL(w));
			ans[1]=w[0].second;
		}
		return ans;
	}
};

2014-06-13

SRM 624 Div1 450 DrivingPlans

01:31 |  SRM 624 Div1 450 DrivingPlans - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 624 Div1 450 DrivingPlans - kojingharangの日記  SRM 624 Div1 450 DrivingPlans - kojingharangの日記 のブックマークコメント

  • 閉路なし重複辺なしで連結なN頂点無向グラフと各辺上を移動するのにかかる時間が与えられる。頂点1→Nまでを最短時間でいくルートが何通りあるか mod 10^9+9 で求めよ。同じ頂点を複数回訪れてもいい。無限なら -1 を返す。
  • N≦2000, 0≦時間≦100000
  • (sampleにある通り,) 最短経路上にある点から時間 0 でいける点があれば行ったり来たりできるので無限通りになる所がポイントか。
  • 組み合わせはなんとなく1→ある頂点に行く組み合わせ数を持っておいて足していくのだろう
  • とりあえずdijkstraで1→(少なくともNを開くまでの)全頂点の最短時間を求めておく
  • 頂点1では1通りとして、1からの所要時間が短い順に頂点を見ていって、そこから出る辺が最短経路に含まれていたら組み合わせ数を足していく感じ
  • そこから時間0で行ける辺があったら-1を返す
  • modとってなかった
  • challenge される
  • (あとで)
  • というかそもそも間違っていた
  • SRM 622 Div1 250 BuildingRoutes と似てて、ある辺(u,v)がS→Eの最短経路集合に含まれるとは
  • cost(S→u)+cost(u→v)+cost(v→E) = cost(S→E)
  • cost(v→E) は S 始点の dijkstra では求まらないけど、この問題の場合無向グラフなので cost(v→E) = cost(E→v)
  • なので E 始点の dijkstra もしておけば O(1) で判定できる
  • 最短経路上にある頂点それぞれについて、時間 0 で行ける辺があるなら return -1
  • すっきり
  • accepted in practice room
// たまにはマクロも
#define ll long long
#define VI vector<ll>
#define PII pair<ll, ll> 
#define REP(i,n) for(int i=0,_n=(n);(i)<(int)_n;++i)
#define ALL(c) (c).begin(), (c).end()
#define MP(a, b) make_pair(a, b)
// Dijkstra略
class DrivingPlans {
	public:
	int count(int N, vector <int> A, vector <int> B, vector <int> C) {
		int M = A.size();
		Dijkstra d(N), r(N);
		REP(i, M) {
			d.add_edge(A[i]-1, B[i]-1, C[i]);
			d.add_edge(B[i]-1, A[i]-1, C[i]);
		}
		r=d;
		int co = d.run(0, N-1);
		r.run(N-1, 0);
		
		vector<PII> p(N);
		REP(i, N) p[i]=MP(d.V[i], i);
		sort(ALL(p));
		VI ways(N);
		ways[0] = 1;
		REP(i, N) {
			int from = p[i].second;
			REP(j, d.G[from].size()) {
				int to=d.G[from][j].to;
				if(d.V[from] + d.G[from][j].cost + r.V[to] == co) {
					ways[to] += ways[from];
					ways[to] %= 1000000009;
					REP(k, d.G[from].size()) if(d.G[from][k].cost==0) return -1;
					REP(k, d.G[to].size()) if(d.G[to][k].cost==0) return -1;
				}
			}
		}
		return ways[N-1];
	}
};

2014-06-09

TCO Round2B 500 SumAndProductPuzzle

19:18 |  TCO Round2B 500 SumAndProductPuzzle - kojingharangの日記 を含むブックマーク はてなブックマーク -  TCO Round2B 500 SumAndProductPuzzle - kojingharangの日記  TCO Round2B 500 SumAndProductPuzzle - kojingharangの日記 のブックマークコメント

  • 「秘密の数 a,b(1≦a≦b)を決める。SuさんにS=a+bを、PriさんにP=a*bを教える。Suさんが「PriさんはSが当てられないはず」と教えることで初めてPriさんはSを特定できる」
  • といったことが起こりうるS in [A, B]の和を求めよ
  • 1≦A≦B≦5,000,000
  • 何言ってんの
  • ■PriがSを特定できないということは.....
  • Ps(S) ... Sに対応するPの候補 として
  • PCanNotTellS(S) ... PriがSを特定できない として
  • PCanNotTellS(S) == P=pq(1≦p≦q)と2通り以上で表せる for any P in Ps(S) ということであろう
  • Ps(S) = {1*(S-1), 2*(S-2), ...}
  • とりあえずどんなPでも1*Pと分割できるので、P=K*(P/K) (K≧2)とも表せる時は必ず複数通りで表せる。
  • P=1*P の時は P が合成数のときだけ複数通りで表せる.
  • ということで PCanNotTellS(S) == S-1が合成数
  • ■「Suさんが「PriさんはSが当てられないはず」と教えることで初めてPriさんはSを特定できる」とは
  • 上記の状況かつ, S に対応する P のうちどれかは, その P に対応する S のうち PCanNotTellS(S) なものが丁度1コある
  • ぽいな
  • とここまでクリアじゃないけどとりあえずこんな感じでシミュレーションを書いてみよう
  • ていうか S に対応する P ってめっちゃあるじゃん
  • ループが深くなって何をしてんのかよく分からなくなってるうちに終了。
  • (あとで)
  • S に対応する P は 1*(S-1), 2*(S-2), ... と沢山あるけど P = 1*(S-1) に対してだけ何か判定してるように見える
  • ...
  • PCanNotTellS と同様に 2*(S-2) 以降は常に「PCanNotTellS(S) なものが丁度1コ」じゃないのかもしれん
  • S に対応する P として K*(S-K) (K≧2)を考えると, その P を P = 1*K(S-K) = K*(S-K) と分けることができて
  • 対応する S としては 1+K(S-K) と K+(S-K) があることになる. そのどちらも
  • 「1+K(S-K)-1は合成数」, 「K+(S-K)-1は合成数 (→S-1が合成数という前提から)」を満たすので K≧2の場合は丁度1コになりえない
  • となるので、P=1*(S-1) だけについて「PCanNotTellS(S) なものが丁度1コ」かどうか考えればいい.
  • となんとか捻り出せた
  • お、おう
  • P=S-1 に対応する S = {p+P/p, 1≦p≦P/p} のうち p+P/p-1 が合成数のやつが 1 コかどうか計算すればいいので
  • ありうる p を全探索しつつ p+P/p-1 が合成数かどうかをカウントして
  • S in [A, B] のうち S-1が合成数 && カウント[S-1]==1のものの和を求めると終了
  • ということになった。
  • 篩が O(NloglogN), 調和数がO(logN)なので
  • 全体で O(NlogN)
  • accepted in practice room
  • I hate math yay
class SumAndProductPuzzle {
	public:
	long long getSum(int A, int B) {
		const int N = 5000000;
		vector<bool> isp(N+1, true);
		RANGE(p, 2, sqrt(N+1)+2) if(isp[p])for(int q=p*2;q<N+1;q+=p) isp[q]=false;

		vector<int> cnt(N+1); // cnt[P] ... P=pq, 1≦p≦q のうち p+q-1が合成数なものの数
		RANGE(p, 1, N+1) RANGE(q, p, N/p+1) if(!isp[p+q-1]) cnt[p*q]++;
		ll ans=0;
		RANGE(S, A, B+1) if(!isp[S-1] && cnt[S-1]==1) ans+=S;
		return ans;
	}
};

2014-06-05

SRM 623 Div1 300 UniformBoard

13:24 |  SRM 623 Div1 300 UniformBoard - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 623 Div1 300 UniformBoard - kojingharangの日記  SRM 623 Div1 300 UniformBoard - kojingharangの日記 のブックマークコメント

  • A or P or . が入ったマスがgrid状にNxN個ある. AかPをどこか他の.のマスに移す操作を最大K回できるとき、全部Aになってる最大rectに含まれるAの数を求めよ。
  • 1≦N≦20
  • 全てのrectについて、K手以内で全部Aにできるかどうかを判定するなら O(N^6) →いけそう
  • 内側の . or P のとこは全部外側のAを持ってくるので、外側のAが足りるかどうか
  • 内側の P がある場合、それを外に出すために外側に最低1個 . があるかどうか
  • 必要なら P を出して(1) Aを入れる(1) 回数 numP*2 + numDot ≦ K ならOK
  • 今日は2回目のコンパイルで全sample pass (maxの閉じカッコがなかった)
  • 最大ケースとかいくつかチェック→提出
  • 450 わからんー
  • challenge される
  • あとで
  • 内側のPがある場合、(初期状態ではなく内側の . に外側の A を移動した後で)外側に . が1個以上あればいいのだった。
  • この辺が300ということなのか? うむむ
  • accepted in practice
class UniformBoard {
	public:
	int getBoard(vector <string> B, int K) {
		int N=B.size();
		int ans = 0;
		REP(sx, N) REP(sy, N) RANGE(ex, sx, N) RANGE(ey, sy, N) {
			int iE=0,iP=0,oE=0,oA=0;
			REP(x, N) REP(y, N) {
				if(sx<=x && x<=ex && sy<=y && y<=ey) {
					if(B[x][y]=='P') iP++;
					if(B[x][y]=='.') iE++;
				} else {
					if(B[x][y]=='A') oA++;
					if(B[x][y]=='.') oE++;
				}
			}
			//if(iE+iP<=oA && (iP==0 || oE>0) && iE+2*iP <= K) ans=max(ans, (ex-sx+1)*(ey-sy+1)); //だめ
			if(iE+iP<=oA && (iP==0 || iE+oE>0) && iE+2*iP <= K) ans=max(ans, (ex-sx+1)*(ey-sy+1));
		}
		return ans;
	}
};

2014-05-29

SRM 622 Div1 250 BuildingRoutes

12:38 |  SRM 622 Div1 250 BuildingRoutes - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 622 Div1 250 BuildingRoutes - kojingharangの日記  SRM 622 Div1 250 BuildingRoutes - kojingharangの日記 のブックマークコメント

  • N 頂点、重複しないNxN辺(辺の長さ1〜9, 自分→自分は0)の有向グラフが与えられる。f(辺) = (始点, 終点)の組のうち、その最短経路がその辺を通りうるような組がT個以上あるなら辺の長さ、そうでなければ0 として、f(辺) の和を求めよ。
  • 2≦N≦50
  • まずWFして...
  • 複数ありうる最短経路がどこを通るかはどうやって分かるんだ?
  • ...
  • f(s, e) ... s〜eの最短経路で通りうる辺を塗る関数を定義して再帰させる
  • TLE
  • (あとで)
  • 他の人のをいくつか見るに,
  • i→jの最短経路を w[i][j] として、辺ii→jjがi→jの最短経路に含まれるかどうかは
  • w[i][j] == w[i][ii] + 辺の長さ[ii][jj] + w[jj][j] かどうかで分かる
  • 言われてみればその通りだ。く〜
  • accepted in practice room
class BuildingRoutes {
	public:
	int build(vector <string> D, int T) {
		int N=D.size();
		VVI d(N, VI(N));
		VVI w(N, VI(N));
		REP(i, N)REP(j,N)d[i][j]=w[i][j]=D[i][j]-'0';
		REP(k,N)REP(i, N)REP(j,N) w[i][j]=min(w[i][j], w[i][k]+w[k][j]);
		ll ans = 0;
		REP(i, N)REP(j,N){
			ll v=0; //(本文中とはi/ii, j/jjが逆)
			REP(ii, N)REP(jj,N) if(w[ii][i]+d[i][j]+w[j][jj]==w[ii][jj]) v++;
			if(v>=T) ans+=d[i][j];
		}
		return ans;
	}
};
|