Hatena::Grouptopcoder

kojingharangの日記 RSSフィード

|

2014-03-05

kojingharang20140305

SRM 611 Div1 250 LCMSet

21:26 |  SRM 611 Div1 250 LCMSet - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 611 Div1 250 LCMSet - kojingharangの日記  SRM 611 Div1 250 LCMSet - kojingharangの日記 のブックマークコメント

  • 数列 A, B が与えられる。"Aの任意の部分集合のLCM"の集合 と "Bの任意の部分集合のLCM"の集合 が等しいかどうかを求めよ。
  • |A|≦50, |B|≦50, 数字≦10^9

(しばらく)

  • 素因数分解したとしてサンプル1を考えてみる
  • こんな感じの図(右上)を唸りながら大量に書く(キケン)
  • LCM は和集合をとることに相当する
  • ならば、数列のうち、数列の他の数字から作れるもの("OR"しても変わんないもの)を消して、根源的なものだけ残してみる。
  • それらを比べて同じなら同じ、異なるなら異なる、でいい気がする
  • 数列にある数字それぞれについて、ほかのやつすべてと比べてごにょごにょして判定するのだろうか
  • と考えてたらいつの間にか「ソートしといて今までの max を持っておいてある数字が max からはみ出たら根源的」でいいんじゃね、となる
  • 素因数分解して (素数の配列, 指数の配列)の配列 を作ってめんどくさいコードで比較
  • system test failed
  • いままでの max と比較、だと 5, 6, 15 みたいなときに 15 を根源的じゃないと判定してしまうようだ。
5 = 2^0 * 3^0 * 5^15 : 0 0 1 と表すと

5  : 0 0 1
6  : 1 1 0
15 : 0 1 1  // 5, 6 からは作れないので根源的とすべきだがそう判定されない
  • うむーなるほど。他のやつが含まれるなら和集合をとっていって、その数にならなければ根源的、とすれば良いようだ。
  • accepted in practice room
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}

set<int> h(vector<int>& A) {
	set<int> s;
	REP(i, A.size()) {
		ll l = 1;
		REP(j, A.size()) {
			if(A[j]<A[i] && lcm(A[i], A[j])==A[i]) l=lcm(l, A[j]);
		}
		if(l < A[i]) s.insert(A[i]);
	}
	return s;
}

class LCMSet {
	public:
	string equal(vector <int> A, vector <int> B) {
		return h(A)==h(B) ? "Equal":"Not equal";
	}
};

2014-02-26

SRM 610 Div1 250 TheMatrix

14:48 |  SRM 610 Div1 250 TheMatrix - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 610 Div1 250 TheMatrix - kojingharangの日記  SRM 610 Div1 250 TheMatrix - kojingharangの日記 のブックマークコメント

  • 最大 100x100 の 1 か 0 が書かれた grid が渡される(プレゼント)。市松模様になってる矩形の面積の最大値を求めよ。
  • f(始点(x, y), 右方向|下方向, スタートのマス0|1) = その方向に最大何マス市松模様になってるか
  • を求めたあと、... あれO(N^5)になってしまう
  • あれ んーー
  • おわり
  • あとで
  • y0, y1 を決めた後 x in [0, W] まで見ていきながら y in [y0, y1] が市松模様になってなかったらリセット、なってたらカウンタ++ するなどうまくやると O(N^4)とかO(N^3)とか
  • それか、まず市松模様状に反転してしまえば矩形内の合計値が w*h か 0 になるかをチェックする問題になる。O(N^4)
  • accepted in practice room
class TheMatrix {
	public:
	int MaxArea(vector <string> B) {
        int W=B[0].size();
        int H=B.size();
        Sum2D<ll> sum(W, H); // ライブラリ乙
        REP(x, W) REP(y, H) sum.data[y][x] = B[y][x]-'0';
        REP(x, W) REP(y, H) if((x+y)%2) sum.data[y][x]^=1;
        sum.prepare();
        int ans = 0;
        REP(x, W) REP(y, H) RANGE(x2, x, W) RANGE(y2, y, H) {
            int w = x2-x+1;
            int h = y2-y+1;
            ll s = sum.sum(x, y, x2+1, y2+1);
            if(s==0 || s==w*h) ans=max(ans, w*h);
        }
        return ans;
	}
};

2014-02-09

AtCoder Regular Contest #017 D - ARCたんクッキー

01:06 |  AtCoder Regular Contest #017 D - ARCたんクッキー - kojingharangの日記 を含むブックマーク はてなブックマーク -  AtCoder Regular Contest #017 D - ARCたんクッキー - kojingharangの日記  AtCoder Regular Contest #017 D - ARCたんクッキー - kojingharangの日記 のブックマークコメント

  • N 個の数字と M 個の操作が与えられるので実行しろ。操作は範囲[L, R]の位置の数字たちに T を足す or 範囲[L, R]の位置の数字たちのgcdを印字する。
  • 1≦N, M≦100,000
  • 部分点 足す操作の時 L=R のやつができたら 30 点
  • Segment tree をそのまま使って 30 点ゲット

  • あとで
  • gcd(a, b) = gcd(a, b-a) なので
  • gcd(a, b, c, d) = gcd(a, gcd(b, gcd(c, d)))
  • = gcd(a, gcd(b, gcd(c, d-c)))
  • = gcd(a, gcd(gcd(b, c), d-c)))
  • = gcd(a, gcd(gcd(b, c-b), d-c)))
  • = gcd(gcd(gcd(a, b), c-b), d-c)))
  • = gcd(gcd(gcd(a, b-a), c-b), d-c)))
  • = gcd(a, b-a, c-b, d-c) ... もやもや
  • となるので差(b-a, c-b, ...)と先頭の値 a がわかってれば良いようだ
  • accepted
  • (SegTree の遅延評価とかはまだ分かってない)
int main() {
	ios::sync_with_stdio(false);
	ll N, M, v, T, L, R;
	while(cin>>N) {
		SegTree<Gcd> st(N+1);
		SegTree<Add> cum(N+1);
		VI cur(N), diff(N+1);
		REP(i, N) {
			cin>>v;
			cur[i] = v;
		}
		diff[0]=cur[0];
		cum.update(0, diff[0]);
		RANGE(i, 1, N) {
			diff[i]=cur[i]-cur[i-1];
			cum.update(i, diff[i]);
			st.update(i, diff[i]);
		}
		cin>>M;
		REP(i, M) {
			cin>>T>>L>>R;
			L--; R--;
			if(T==0) {
				ll head = cum.query(0, L+1);
				ll v = st.query(L+1, R+1);
				cout<<Gcd::op(head, v)<<endl;
			} else {
				diff[L] += T;
				diff[R+1] -= T;
				st.update(L, diff[L]);
				st.update(R+1, diff[R+1]);
				cum.update(L, diff[L]);
				cum.update(R+1, diff[R+1]);
			}
		}
	}
	
	return 0;
}

2014-02-03

SRM 607 Div1 250 PalindromicSubstringsDiv1

23:28 |  SRM 607 Div1 250 PalindromicSubstringsDiv1 - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 607 Div1 250 PalindromicSubstringsDiv1 - kojingharangの日記  SRM 607 Div1 250 PalindromicSubstringsDiv1 - kojingharangの日記 のブックマークコメント

  • '?' か a-z を含む文字列 S がある。'?' をランダムに a-z のどれかに置換したとき、回文になってる部分文字列の個数の期待値を求めよ。
  • 1≦|S|≦5000
  • 15分くらい何の方針も立たず
  • 部分文字列ごとに独立に期待値(そうなる確率 * 1個)を求めて足せば良いと気づく
  • (部分問題とか状態、パラメータの依存関係を的確に明らかにしてくのがこういうののコツらしい)
  • すると, ある部分文字列に対して O(N) で期待値が分かる
  • 両端から見ていってどっちかが ? なら 1/26, そうでなくて同じ文字なら 1, 異なる文字なら 0 を掛けてく
  • 部分文字列は N(N+1)/2 個なので O(N^3) (実際はもっと小さいんだっけ)
  • 5000^3 はあれなので O(N^2) に落としたい
  • S[s,e]が回文であるとは S[s]=S[e] かつ S[s+1, e-1] が回文みたいにして O(N^2) 化
  • デバッグに時間がかかる (;_;)
  • accepted
double dp[5010][5010]; // dp[a][b] ... s[a, b]が回文になる確率
class PalindromicSubstringsDiv1 {
	public:
	double expectedPalindromes(vector <string> S1, vector <string> S2) {
		CLEAR(dp, 0);
		string s = accumulate(ALL(S1), string(""))+accumulate(ALL(S2), string(""));
		int N=s.size();
		double ans=0;
		RANGE(L, 1, N+1) REP(i, N-L+1) {
			char a=s[i], b=s[i+L-1];
			double lans=1.0/26;
			if(L==1) lans=1.0;
			else {
				if(a!='?' && b!='?' && a==b) lans = 1.0;
				if(a!='?' && b!='?' && a!=b) lans = 0.0;
			}
			double inner = L<=2 ? 1.0 : dp[i+1][i+L-1-1];
			dp[i][i+L-1] = lans * inner;
			ans += dp[i][i+L-1];
		}
		return ans;
	}
};

2014-02-02

お誕生日コンテスト E. 排他的☆論理和っ!!

18:49 |  お誕生日コンテスト E. 排他的☆論理和っ!! - kojingharangの日記 を含むブックマーク はてなブックマーク -  お誕生日コンテスト E. 排他的☆論理和っ!! - kojingharangの日記  お誕生日コンテスト E. 排他的☆論理和っ!! - kojingharangの日記 のブックマークコメント

  • とりあえず hexdump で見てみる
  • 0x70 は位置的にスペースっぽい
  • 0x5a は位置的に LF っぽい
  • {0x30, -0x50} を繰り返し足してくとそれっぽい?→微妙におかしい
  • ...
  • 加算じゃなくて XOR を使うのか
  • 長さLの鍵を、復号すると数字かスペースかLFになるように求める。→ sample 0, 1 はいいけど残りは複数通りある
  • まさか探索すんのか( ゚д゚) そんなつもりでは
  • 先頭 N バイトまでに関して、validな問題文かどうかを判定するルーチンを書いて枝刈り
  • 書いてコピペ
  • 書いたけど最後の1個だけ間違ってるようだ。
  • ずっとsample10のデバッグしてるけど特におかしくない
  • (あとで)
  • atcoderの結果は順番がランダムに並び替わってるのか( ゚д゚)
  • sample4が少し古い結果を使ってたようだ。...
  • accepted

// めんどくさい
bool isNum(const string& s, int& idx, int limit, int& out) {
	if(idx>=limit) return false;
	if(!isdigit(s[idx])) return false;
	out=0;
	int read=0;
	while(idx<limit && isdigit(s[idx])) {
		out=out*10+(s[idx]-'0');
		idx++;
		read++;
	}
	if(out==0 && read>1) return false;
	return true;
}

string realAns;
// めんどくさい
bool valid2(const string& s, int limit, bool final=false) {
	int idx=0;
	int v;
	if(idx>=limit) return !final;
	if(!isNum(s, idx, limit, v)) return false;
	if(!(1<=v&&v<=200)) return false;
	if(idx>=limit) return !final;
	if(s[idx++]!=0x0a) return false;
	int loop = v;
	stringstream ans;
	REP(i, loop) {
		int a, b;
		if(idx>=limit) return !final;
		if(!isNum(s, idx, limit, v)) return false;
		if(!(1<=v&&v<=10)) return false;
		if(idx>=limit) return !final;
		if(s[idx++]!=' ') return false;
		a=v;
		
		if(idx>=limit) return !final;
		if(!isNum(s, idx, limit, v)) return false;
		if(!(1<=v&&v<=10)) return false;
		if(idx>=limit) return !final;
		if(s[idx++]!=0x0a) return false;
		b=v;
		ans<<"ans.PB("<<(a^b)<<");";
	}
	if(final && idx!=limit) return false;
	if(final) realAns = ans.str();
	return true;
}


VVI w;
string src, validAns;
int cur[128];
int L=128;
VVI ans;
void dfs(int idx) {
	if(validAns.size()) return;
	if(idx==ans.size()) {
		string ss=src;
		REP(j, ss.size()) ss[j]^=ans[j%L][cur[j%L]];
		if(valid2(ss, ss.size(), true)) {
			cout<<"//Found valid ans "<<endl;
			validAns=ss;
		}
		return;
	}
	REP(i, ans[idx].size()) {
		cur[idx]=i;
		bool go=true;
		{
			string ss=src;
			REP(j, ss.size()) ss[j]^=ans[j%L][cur[j%L]];
			if(!valid2(ss, idx+1)) go=false;
		}
		if(go) dfs(idx+1);
	}
}

bool validChars(const VI& s) {
	int cnt=0;
	REP(i, s.size()) {
		if(isdigit(s[i]) || s[i]==' ' || s[i]==0x0a) cnt++;
	}
	return cnt==s.size();
}

int main(int argc, char**argv) {
	//ios::sync_with_stdio(false);
	int c;
	string s;
	int co=0;
	FILE* fin = fopen(argv[1], "rb");
	w = VVI(L, VI());
	while((c=fgetc(fin))!=EOF) {
		w[co%L].PB(c);
		s.PB(c);
		co++;
	}
	src=s;
	ans = VVI(L, VI());
	REP(l, L) {
		REP(a, 256) {
			VI ss=w[l];
			REP(i, ss.size()) ss[i] = (ss[i] ^ a) & 0xFF;
			if(validChars(ss)) ans[l].PB(a);
		}
	}
	dfs(0);
	
	cout<<realAns<<endl;
	
	return 0;
}
|