Hatena::Grouptopcoder

kojingharangの日記 RSSフィード

 | 

2012-04-15

GCJ2012 Qualification B. Dancing With the Googlers

10:55 |  GCJ2012 Qualification B. Dancing With the Googlers - kojingharangの日記 を含むブックマーク はてなブックマーク -  GCJ2012 Qualification B. Dancing With the Googlers - kojingharangの日記  GCJ2012 Qualification B. Dancing With the Googlers - kojingharangの日記 のブックマークコメント

  • 数字3つの tuple の合計値が N 個与えられる。N 個の元の tuple のうち、tuple の最大値 max, 最小値 min として max - min == 2 なのが S 個(surprisingと呼ぶ)、残りは max-min < 2 のとき、
  • 最大値が p 以上のものの数の最大値を求める問題。
  • 合計値が x のとき、surprising なら最大値は (x+2)/3, そうじゃなければ (x+4)/3 と一意に決まる
  • ので、合計値を降順にソートして greedy に処理する。
  • 全部 not surprising としておいて、p 以上のはそのままカウント
  • 超えてないもので surprising とすれば p 以上になるものは surprising 券がまだ使えるならカウント
int main() {
	int test_cases;
	cin>>test_cases;
	REP(ttt, test_cases) {
		int N, S, P;
		cin>>N>>S>>P;
		VI T(N);
		REP(i, N) cin>>T[i];
		sort(ALLR(T));
		//cout<<T<<endl;
		int ans = 0;
		REP(i, N) {
			if((T[i]+2)/3 >= P) ans++;
			else if(S>0 && T[i]>=2 && (T[i]+4)/3 >= P) {
				ans++;
				S--;
			}
		}
		cout<<"Case #"<<ttt+1<<": "<<ans<<endl;
	}
	return 0;
}

GCJ2012 Qualification C. Recycled Numbers

10:55 |  GCJ2012 Qualification C. Recycled Numbers - kojingharangの日記 を含むブックマーク はてなブックマーク -  GCJ2012 Qualification C. Recycled Numbers - kojingharangの日記  GCJ2012 Qualification C. Recycled Numbers - kojingharangの日記 のブックマークコメント

  • A <= n <= B <= 2000000 な n の各桁を回転させた数字 m が A <= n < m <= B になるような (n, m) の個数を重複なく求める問題。
  • 2000000 だから微妙かと思いつつ特に工夫なく書いたら大丈夫そうなので出した
  • 重複をカウントしない処理が抜けていて large 落ちた(´・ω・`)
  • small のテストケースに頼ってはいけない...
  • と思ったら small どころか sample 1111 2222 -> 287 とあって親切にも "Are we sure about the output to Case #4? Yes, we're sure about the output to Case #4." と書いてあったのであった。
  • diff が 288 と 287 を等しいと判定してしまった ... 目diff failed.

↓あとで

int main() {
	int test_cases;
	cin>>test_cases;
	REP(ttt, test_cases) {
		int A, B;
		cin>>A>>B;
		int sp=1;
		VI tb;
		VI tb2;
		while(B>sp*10) {
			sp*=10;
			tb.PB(sp);
		}
		REP(i, tb.size()) tb2.PB(sp*10/tb[i]);
		int ans=0;
		for(int i=A;i<=B;i++) {
			//cout<<"i "<<i<<endl;
			set<int> used;
			REP(j, tb.size()) {
				int b = i/tb[j] + (i%tb[j])*tb2[j];
				if(i<b && A<=b && b<=B && /* ADDED */ used.insert(b).second) {
					ans++;
					//cout<<b<<endl;
				}
			}
		}
		cout<<"Case #"<<ttt+1<<": "<<ans<<endl;
	}
	return 0;
}

GCJ2012 Qualification D. Hall of Mirrors

10:55 |  GCJ2012 Qualification D. Hall of Mirrors - kojingharangの日記 を含むブックマーク はてなブックマーク -  GCJ2012 Qualification D. Hall of Mirrors - kojingharangの日記  GCJ2012 Qualification D. Hall of Mirrors - kojingharangの日記 のブックマークコメント

  • 2次元の世界に鏡があって自分が立ってるので自分がいくつ見えるかを求める問題。
  • 難しそうなので部屋の壁にしか鏡がない small を考える
  • 反射するイコール対称な位置に自分がいるということなので鏡の向こうの自分をたくさん配置する
  • 各自分について、実際の自分の位置から見えるかどうか判定する。
  • 距離が近いほうから見ていって、同一直線上にあったらカウントしない
  • サンプルの 3x4 の部屋で 68 人も見える事にびっくり。実際のところは頭の幅とかあるからそんなに見えないかもしれないけど見てみたら怖そう...
  • small 通ったけど large わからん。解説をみる
int main() {
	int test_cases;
	cin>>test_cases;
	REP(ttt, test_cases) {
		int H,W,D;
		cin>>H>>W>>D;
		//cout<<H<<" "<<W<<" "<<D<<endl;
		
		int ox=0, oy=0;
		REP(h, H) {
			string s;
			cin>>s;
			REP(i, s.size()) if(s[i]=='X') {ox=i; oy=h;}
		}
		ox--;oy--;
		//cout<<ox<<" "<<oy<<endl;
		
		int ww = 100/(W-2)+1;
		int hh = 100/(H-2)+1;
		int ans = 0;
		vector<pair<int, PII> > l;
		for(int x=-ww;x<=ww;x++) {
			for(int y=-hh;y<=hh;y++) {
				int MX = x*(W-2)+(x%2==0 ? ox : W-2-ox-1)-ox;
				int MY = y*(H-2)+(y%2==0 ? oy : H-2-oy-1)-oy;
				int d = MX*MX+MY*MY;
				//if(d!=0 && d<=D*D) cout<<d<<"  "<<x<<" "<<y<<"    "<<MX<<" "<<MY<<endl;
				if(d!=0 && d<=D*D) l.PB(MP(d, MP(MX, MY)));
			}
		}
		//map<int, int> dh;
		
		sort(ALL(l));
		vector<PII> used;
		REP(i, l.size()) {
			int d = l[i].first;
			int MX=l[i].second.first;
			int MY=l[i].second.second;
			int ok=1;
			//cout<<"try "<<d<<" "<<MX<<" "<<MY<<endl;
			REP(j, used.size()) {
				int ux = used[j].first;
				int uy = used[j].second;
				if(MX*ux<0) continue;
				if(MY*uy<0) continue;
				if(ux==0) {
					if(MX!=0) continue;
					else {ok=0;break;}
				}
				if(uy==0) {
					if(MY!=0) continue;
					else {ok=0;break;}
				}
				int ug = GCD(ux, uy);
				int mg = GCD(MX, MY);
				if(ux/ug==MX/mg && uy/ug==MY/mg) {ok=0;break;}
			}
			if(ok) {
				//cout<<"X "<<d<<" "<<MX<<" "<<MY<<endl;
				//cout<<MX<<"\t"<<MY<<(MX==0 ? MY*10000000 : (double)MY/MX+(MX>0?1000:-1000))<<endl;
				//cout<<MX<<"\t"<<MY<<endl;
				ans++;
				used.PB(l[i].second);
				//dh[d]++;
			}
		}
		
		//vector<double> aa;
		//FOR(e, used) aa.PB(e->first==0 ? (double)e->second*1000000 : (double)e->second/e->first+(e->first>0?1000:-1000));
		//sort(ALL(aa));
		//REP(i, aa.size()-1) if(abs(aa[i]-aa[i+1]) < 0.00001) cout<<"ERROR"<<aa[i]<<" "<<aa[i+1]<<endl;
		//cout<<dh<<endl;
		cout<<"Case #"<<ttt+1<<": "<<ans<<endl;
	}
	return 0;
}

TCO12 Round1C 500 PasswordXGrid

09:06 |  TCO12 Round1C 500 PasswordXGrid - kojingharangの日記 を含むブックマーク はてなブックマーク -  TCO12 Round1C 500 PasswordXGrid - kojingharangの日記  TCO12 Round1C 500 PasswordXGrid - kojingharangの日記 のブックマークコメント

  • W x H グリッドの各辺にコストが与えられて、左上頂点から右下頂点に向かって後戻りしないで長さ W+H のパスで行くとき、どんなパスを通っても通ったとこのコストの和が同じになるように各辺のコストを大きくしたい。そうしたときのコストの和の最小値を求める問題。
  • 一番コストが高いパスのコストの和が答えなので左上からDP
  • 残り20分くらいしかなくて 250 がややこしかったので厳しいかなと思ったけど簡単でよかった
class PasswordXGrid {
	public:
	int minSum(vector <string> H, vector <string> V) {
		int N=H.size()-1;
		int M=H[0].size();
		VVI m(N+1, VI(M+1));
		cout<<M<<" "<<N<<endl;
		REP(y, N+1) {
			REP(x, M+1) {
				if(x-1>=0) m[y][x] = max(m[y][x], m[y][x-1]+H[y][x-1]-'0');
				if(y-1>=0) m[y][x] = max(m[y][x], m[y-1][x]+V[y-1][x]-'0');
			}
		}
		//cout<<m<<endl;
		return m[N][M];
	}
};

TCO12 Round1C 250 PasswordXGuessing

09:06 |  TCO12 Round1C 250 PasswordXGuessing - kojingharangの日記 を含むブックマーク はてなブックマーク -  TCO12 Round1C 250 PasswordXGuessing - kojingharangの日記  TCO12 Round1C 250 PasswordXGuessing - kojingharangの日記 のブックマークコメント

  • パスワードが決まってる(けど与えられない)。文字列がいくつか与えられて、各1文字を除いてそのパスワードと一致している。可能なパスワードが何通りあるか。
  • なにこれ難しいと焦る
  • 各桁の数字の候補を桁x10の配列で管理
  • 最初の文字列について、間違ってる文字の位置を仮定する
  • 間違ってるとこはその文字以外残るし、あってるとこはその文字だけ残る
  • 残りの文字列について、候補に含まれていることを確認する。だめなら仮定した桁はだめなので次。
  • その際、最初に仮定したとこ以外は正しいか間違ってるかわかるので優先して処理
  • その結果その文字列に関して間違ってる桁数によって決まってないとこが間違ってるかどうか決まるので間違ってたら候補を消す
  • 各桁の候補数を掛けて各仮定について和をとってのが答え
  • あとで他人のを見て、候補とか考えないで間違ってる桁を 9 通り試せばいいだけと分かった。
  • パスワードを仮定すれば残りの文字列で間違ってる桁がちゃんと1コかどうかを確かめて OK なら ans++ するだけでしたちゃんちゃん
  • long long はひっかけ
class PasswordXGuessing {
	public:
	long long howMany(vector <string> G) {
		int N=G.size();
		int X=G[0].size();
		ll ans=0;
		//cout<<endl;
		REP(wr, X) {
			VI can(X*10, 1);
			REP(j, X) {
				if(j==wr) {
					can[j*10+G[0][j]-'0']=0;
				} else {
					REP(z, 10) if(z!=G[0][j]-'0') can[j*10+z]=0;
				}
			}
			//cout<<can<<endl;
			int ok=1;
			for(int i=1;i<N;i++) {
				//cout<<i<<endl;
				int x=0;
				VI w(X);
				REP(j, X) {
					if(can[j*10+G[i][j]-'0']) {
						if(j!=wr) w[j]=1;
					} else {
						x++;
						w[j]=1;
					}
				}
				//cout<<i<<endl;
				if(x>1) {ok=0;break;}
				if(x==1) {
					REP(j, X) if(w[j]==0) REP(zz, 10) if(zz!=G[i][j]-'0') can[j*10+zz]=0;
				}
				if(x==0) {
					REP(j, X) if(w[j]==0) can[j*10+G[i][j]-'0']=0;
				}
				if(!ok) break;
			}
			if(!ok) continue;
			ll lans=1;
			REP(j, X) {
				ll a=0;
				REP(z, 10) a+=can[j*10+z];
				lans *= a;
			}
			//cout<<can<<endl;
			//cout<<"+ "<<lans<<endl;
			ans += lans;
		}
		return ans;
	}
};
 |