Hatena::Grouptopcoder

kojingharangの日記 RSSフィード

|

2014-01-31

SRM 606 Div1 250 EllysNumberGuessing

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

  • 隠された数字 X があって、推測値 G[i] と差の絶対値 A[i]=|X-G[i]| が与えられる。
  • X が一意に定まるならその数字、定まらないなら -1, どれかの A[i] が矛盾してるなら -2 を返せ。
  • 1≦X,G[i]≦1,000,000,000, 1≦A[i]≦999,999,999, 1≦G.size≦50
  • X の候補は G[0]+A[0], G[0]-A[0] の 2 つだから、それぞれについて残りの G, A をみてけばいいっすね
  • と思いつつスラスラ書き始められないのがつらぽよ
  • 制約を満たす答えが 0 個なら矛盾, 1個なら一意, 2個なら定まらないとすればよいのか。
  • accepted
class EllysNumberGuessing {
  public:
  int getNumber(vector <int> G, vector <int> A) {
    ll limit=1000000000;
        VI w(2);
        w[0]=G[0]-A[0];
        w[1]=G[0]+A[0];
        VI ans;
        REP(j, 2) {
            bool has=true;
            REP(i, G.size()) {
                ll a=G[i]-A[i];
                ll b=G[i]+A[i];
                if(!(w[j]==a || w[j]==b)) has=false;
            }
            if(has) {
                if(1<=w[j] && w[j]<=limit) {
                    ans.PB(w[j]);
                }
            }
        }
        if(ans.size()==0) return -2;
        if(ans.size()==1) return ans[0];
        if(ans.size()==2) return -1;
        return -1;
  }
};

2014-01-30

SRM 606 Div1 450 EllysPairing

20:46 |  SRM 606 Div1 450 EllysPairing - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 606 Div1 450 EllysPairing - kojingharangの日記  SRM 606 Div1 450 EllysPairing - kojingharangの日記 のブックマークコメント

  • 「数列の計算式 (X[0], X[i+1]=(A[j]*X[i]+B[j])%M) と数列の長さ」が N 組与えられる。
  • 数列たちに含まれる全ての数を使ってペアを作る。最大何個作れるか。ペア内の数字は異なること。
  • 1≦N≦50, Mは2の累乗, 2≦M≦2^30, 0≦A[j], B[j]≦M-1, 1≦各数列の長さ≦1,000,000
  • メモリ制限 16MB
  • サンプルからするに、出現回数が一番多いものの出現回数≦数字の数/2なら数字が異なる制約はクリアできるっぽい。
  • そうじゃないときはそれなりに減る。
  • 出現回数 - 制約なしの時のペア数 - 制約なしの時ペアから余る個数 だけ減る。
  • これでとりあえず出現回数を全部数えるコードを書いて正しそうなことを確認。
  • M が大きいのでどうやって絞るか
  • 全数列を生成するのに 50,000,000 回
  • メモリ 16MB までなら int 3*10^6 個くらいとれるので
  • [0, M) を 10^9 / (3*10^6) = 333 個に分けて 333 回全数列を生成して数える→1.6*10^10→無理
  • .....
  • ある数字 Y が沢山出現するということは Y はどこかの数列で周期 4 以内くらいで出現するはず
  • 最初の 4 項の数字だけ数えるようにしてみる
  • failed system test
  • (あとで)
  • 最初はループしてなくて途中からループする場合があるっぽい。ぐぬぅ
  • TLを眺める

  • majorityを求めるアルゴリズムがあるらしい。ほ〜
  • accepted in practice
class EllysPairing {
	public:
	int getMax(int M, vector <int> count, vector <int> first, vector <int> mult, vector <int> add) {
		int N=count.size();
        int co=0;
        ll maj=-1;
        REP(i, N) {
            uint32_t v = first[i];
            REP(loop, count[i]) {
                if(v==maj) co++;
                else if(co==0) maj=v,co=1; else co--;
                v = (v * mult[i] + add[i]) & (M-1);
            }
        }
        co=0;
        REP(i, N) {
            uint32_t v = first[i];
            REP(loop, count[i]) {
                if(maj==v) co++;
                v = (v * mult[i] + add[i]) & (M-1);
            }
        }
        int total=accumulate(ALL(count), 0);
        int th = total/2;
        if(co>th) {
            int rest = (co-th) - (total-th*2);
            th-=rest;
        }
        return th;
	}
};
  • 別解で、数字の下位 16bit だけを対象に集計(合計)したやつと上位 16bit だけを対象に集計したやつを別々に考えてそれぞれで出現回数が多かった数字が過半数候補、というのもあるらしい。
  • 下位 16bit では最多ではないけど、全体で最大、という見逃される数はないのか??
  • 2つに分かれるときが一番きわどくて、
  • その2つの合計≦全体の半数 なら 見逃されたやつ1個の個数<その2つの合計(≦全体の半数),
  • その2つの合計>全体の半数 なら (見逃されたやつ1個の個数≦)残りの合計≦全体の半数
  • なので見逃されたやつの個数が全体の半数を超えることはない
  • おー
  • accepted in practice
int h[(1<<16)+1];
class EllysPairing {
	public:
	int getMax(int M, vector <int> count, vector <int> first, vector <int> mult, vector <int> add) {
		int N=count.size();
        CLEAR(h, 0);
        REP(i, N) {
            VI w;
            uint32_t v = first[i];
            REP(loop, count[i]) {
                h[v&((1<<16)-1)]++;
                v = (v * mult[i] + add[i]) & (M-1);
            }
        }
        ll lo=-1, loi=-1;
        REP(i, 1<<16) if(lo<h[i]){lo=h[i];loi=i;}
        
        CLEAR(h, 0);
        REP(i, N) {
            VI w;
            uint32_t v = first[i];
            REP(loop, count[i]) {
                h[v>>16]++;
                v = (v * mult[i] + add[i]) & (M-1);
            }
        }
        ll hi=-1, hii=-1;
        REP(i, 1<<16) if(hi<h[i]){hi=h[i];hii=i;}
        
        int maj = (hii<<16)+loi, mx=0;
        REP(i, N) {
            VI w;
            uint32_t v = first[i];
            REP(loop, count[i]) {
                if(v==maj) mx++;
                v = (v * mult[i] + add[i]) & (M-1);
            }
        }
        
        int total=accumulate(ALL(count), 0);
        int th = total/2;
        if(mx>th) {
            int rest = (mx-th) - (total-th*2);
            th-=rest;
//            cout<<"--- "<<rest<<endl;
        }
        return th;
	}
};

2014-01-13

Codeforces #223 Div2 C. Sereja and Prefixes

23:18 |  Codeforces #223 Div2 C. Sereja and Prefixes - kojingharangの日記 を含むブックマーク はてなブックマーク -  Codeforces #223 Div2 C. Sereja and Prefixes - kojingharangの日記  Codeforces #223 Div2 C. Sereja and Prefixes - kojingharangの日記 のブックマークコメント

  • 空の列から始めて、(1)数字 X[i] を末尾に追記 または (2) 先頭から L[i] 文字を C[i] 回末尾に追記 の操作を合計 M 回する。
  • 出来上がった列の I[i] 番目の数字を N 個出力する問題。
  • 1≦X[i], L[i], I[i], N, M≦100,000
  • 1≦C[i]≦10,000

  • i 回目の操作後の列の長さ len[i] を覚えておく
  • len の I[i] 以上の最初のインデックス d を2分探索で求めて、I[i] がどの操作で生まれたものかを特定する。
  • (1) なら追記した数字 X[d]、(2) なら先頭の (I[i]-1-len[d-1]) % L[d-1] の位置の文字。
  • コピー元の長さ≦100,000なのでそこまでは全部作って覚えておく
  • accepted

ll p[100010][3];
ll w[100010];
ll len[100010];
int main() {
	ios::sync_with_stdio(false);
	ll N,M;
	while(cin>>M) {
		len[0]=0;
		REP(i, M) {
			cin>>p[i][0];
			if(p[i][0]==1) {
				cin>>p[i][1];
				len[i+1] = len[i]+1;
				if(len[i]<100000) w[len[i]] = p[i][1];
			}
			if(p[i][0]==2) {
				cin>>p[i][1]>>p[i][2];
				len[i+1] = len[i]+p[i][1]*p[i][2];
				for(int j=0;j<p[i][1]*p[i][2] && len[i]+j<100000;j++) {
					w[len[i]+j]=w[j%p[i][1]];
				}
			}
		}
		cin>>N;
		VI ans(N);
		REP(i, N) {
			ll idx;
			cin>>idx;
			int d = distance(&len[0], lower_bound(&len[0], &len[M+1], idx));
			if(p[d-1][0]==1) {
				assert(idx==len[d]);
				ans[i] = p[d-1][1];
			} else {
				int j = (idx-1-len[d-1]) % p[d-1][1];
				ans[i] = w[j];
			}
		}
		
		REP(i, N) {
			if(i) cout<<" ";
			cout<<ans[i];
		}
		cout<<endl;
	}
	
	return 0;
}


Codeforces #223 Div2 D. Sereja and Tree

23:18 |  Codeforces #223 Div2 D. Sereja and Tree - kojingharangの日記 を含むブックマーク はてなブックマーク -  Codeforces #223 Div2 D. Sereja and Tree - kojingharangの日記  Codeforces #223 Div2 D. Sereja and Tree - kojingharangの日記 のブックマークコメント

  • 深さlevel、左からpos番目のノードに対して posが2の累乗なら左右の子、それ以外なら右の子を持つ最大深さNの木がある。各ノードには空集合が入っている。
  • 以下の2つの命令が計M個くるので処理する問題。
  • (1) level[i], posL[i]〜posR[i] のノードに入ってる集合に x[i] を追加する
  • (2) level[i], pos[i] のノードとそのsubtreeの集合の和集合の要素数を印字する
  • 1≦N,M≦7000
  • 1≦x[i]≦10^6
  • ややこしや
  • もう寝ようかな
  • ややこしや
  • 図が完全二分木じゃないのは間違ってる?
  • よく読んだら2つの子を持つのは2の累乗のときだけ, か...
  • なのでノード数は 2^7000 とかにはならない。深さ7000の葉の個数は109332個
  • (1) が来た時
  • data[i][level][3] = {L, R, X} ... i番目の追加命令では深さlevelに対して L≦pos≦R に X を追加した
  • と覚えておけば (2) のとき O(MlogM) で答えが出せるからそうしよう
  • →256MB に入らない
  • おしまい
  • あとで
  • 逆に(2)が来た時に全深さに対して pos の範囲を計算しといて追加命令の範囲と共通箇所があればカウント
  • だとメモリが少なくて済む
  • なるほど
  • accepted in practice
int chL[110000]; // L child, 1base
int chR[110000]; // R child, 1base
int rL[7007];
int rR[7007];
int T[7007];
int L[7007];
int R[7007];
int X[7007];

// 109332
int main() {
	ios::sync_with_stdio(false);
	ll N,M;
	ll type,t,v;
	while(cin>>N>>M) {
		int cs=1;
		REP(i, 110000) {
			int ch = (POPCOUNT(i+1)==1) ? 2:1;
			chL[i+1]=ch==2?cs++:-1;
			chR[i+1]=cs++;
		}
		int hints=0;
		REP(i, M) {
			cin>>type;
			if(type==1) {
				cin>>T[hints]>>L[hints]>>R[hints]>>X[hints];
				hints++;
			} else {
				cin>>t>>v;
				REP(j, t) rL[j]=rR[j]=-1;
				rL[t]=rR[t]=v;
				RANGE(j, t, N) {
					rL[j+1]=chL[rL[j]]==-1 ? chR[rL[j]] : chL[rL[j]];
					rR[j+1]=chR[rR[j]];
				}
				map<int, int> ma;
				REP(hi, hints) {
					if(rL[T[hi]]==-1) continue;
					bool in = !(R[hi]<rL[T[hi]] || rR[T[hi]]<L[hi]);
					if(in) ma[X[hi]]=1;
				}
				cout<<ma.size()<<endl;
			}
		}
	}
	
	return 0;
}

2014-01-12

SRM 604 Div1 250 PowerOfThree

18:24 |  SRM 604 Div1 250 PowerOfThree - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 604 Div1 250 PowerOfThree - kojingharangの日記  SRM 604 Div1 250 PowerOfThree - kojingharangの日記 のブックマークコメント

  • 原点から始めて (x, y) に行きたい。kステップ目は 3^(k-1) だけ上下左右どれかに必ず移動する。(x, y) に行けるかどうか求める。
  • -1,000,000,000≦x, y≦1,000,000,000
  • 最初にどっちに行くかは決められないような気がする(終了後→余りを考えると決められるらしいと判明)
  • なので、逆に x, y から移動距離 3^K で始めて原点に戻る方向で考えてみる
  • 原点より上にいて上下どっちかに行く場合、(移動量が1/3になってくので)上に進むと以後どんなに下に戻っても上に進んだ距離の半分以下の場所にはこれない。
  • (初項1/3,公比1/3の等比数列の和<1/2)
  • なので原点より上にいて上下に進むなら下に行くしかない。横方向も同じ。
  • なので、各ステップで上下に進むか左右に進むかの2通り選べばよい
  • 1,000,000,000<3^19 で、最初に進んだ距離の半分以上は戻れないことを考えると 20 ステップあれば終わるはず。
  • 余裕(?)を見て 21 として, K in [0, 21] のそれぞれに対して 2^K 通りの探索で間に合うはず。
  • accepted

class PowerOfThree {
	public:
	bool f(ll X, ll Y, ll step) {
		if(X==0 && Y==0 && step==0) return true;
		if(step==0) return false;
		return f(X+(X>0?-1:1)*step, Y, step/3) || f(X, Y+(Y>0?-1:1)*step, step/3);
	}
	string ableToGet(int x, int y) {
		bool ans=false;
		if(x==0 && y==0) return "Possible";
		REP(k, 22) {
			ll step=1;
			REP(i, k) step*=3;
			ans = ans || f(x, y, step);
			if(ans) break;
		}
		return ans ? "Possible" : "Impossible";
	}
};

  • あとで
  • そうか余りに注目すると逆にしなくてもどっちに進むべきか or 不可能か分かるのか。

2013-12-30

SRM 602 Div1 250 TypoCoderDiv1

16:15 |  SRM 602 Div1 250 TypoCoderDiv1 - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 602 Div1 250 TypoCoderDiv1 - kojingharangの日記  SRM 602 Div1 250 TypoCoderDiv1 - kojingharangの日記 のブックマークコメント

  • 数値 X から始めて N ゲームする。ゲーム i で数値を ±D[i] 変えられる。0未満になったら0になる。2連続で2200以上にならない条件で, 2200以上と2200未満を最大何回変えられるか?
  • 1≦N≦50, 0≦D[i]≦1,000,000,000, 0≦X≦2199
  • dp[i回目(終了後)][数値] == 最大変化回数 ... だと 50 * 10^9 かかりそうなので工夫しろということか
  • 2連続で2200以上にならないヒントをどう使うか
  • 2200以上になったら、その次の次で 2200 未満に戻って来れないときは捨てていいので 0〜2199 まで持っておけばよさげ
  • 更新式は
  • 新しい数値<2200 なら dp[i+1][新しい数値] max= dp[i][元の数値] (変化回数は変化なし)
  • 新しい数値≧2200 かつ 次の次がある かつ 次の次に減らしたら 2200 未満になるなら dp[i+2][次の次まで足した数値] max= dp[i][今の数値] + 2
  • 最後は
  • 2200 未満で終わる場合は dp[N][数値] (数値<2200)
  • 2200 以上で終わる場合は「N-1ゲーム終了時点で 2200 未満で N ゲーム後に 2200 以上」なので dp[N-1][数値]+1 (数値<2200 and 数値 + D[N-1]≧2200)
  • デバッグに時間がかかる
  • accepted

int dp[51][2210];
class TypoCoderDiv1 {
	public:
	int getmax(vector <int> D, int X) {
		int minf=-(1<<29);
		int N=D.size();
		REP(i, N+1) REP(v, 2201) dp[i][v]=minf;
		dp[0][X]=0;
		REP(i, N) {
			REP(v, 2200) {
				if(dp[i][v]==minf) continue;
				for(int k=-1;k<=1;k+=2) {
					int nv = max(v+k*D[i], 0);
					if(nv>=2200) {
						if(i+1<N) {
							int nnv = max(nv-D[i+1], 0);
							if(nnv < 2200) {
								// 2200未満に戻ってこれるなら更新
								dp[i+2][nnv] = max(dp[i+2][nnv], dp[i][v]+2);
							}
						}
					} else {
						dp[i+1][nv] = max(dp[i+1][nv], dp[i][v]);
					}
				}
			}
		}
		int ans = 0;
		REP(v, 2200) ans=max(ans, dp[N][v]);
		// 最後で 2200 を超える場合
		REP(v, 2200) if(dp[N-1][v]!=minf && v+D[N-1]>=2200) {
			ans=max(ans, dp[N-1][v]+1);
		}
		return ans;
	}
};

  • あとで
  • そうか 2200 以上の状態を入れても 50 * 4400 個だから map を使えばもっと簡潔に書けるのか。
  • accepted in practice

class TypoCoderDiv1 {
	public:
	int getmax(vector <int> D, int X) {
		int N=D.size();
		map<int, int> dp[52];
		dp[0][X]=0;
		REP(i, N+1) FOR(e, dp[i]) for(int d=-1;d<=1;d+=2) {
			int v=e->first;
			int nv = max(0, e->first + d*D[i]);
			int change = v<2200&&nv>=2200 || v>=2200&&nv<2200;
			if(e->first>=2200 && nv<2200 || e->first<2200)
				dp[i+1][nv] = max(dp[i+1][nv], change + e->second);
		}
		int ans=0;
		FOR(e, dp[N]) ans=max(ans, e->second);
		return ans;
	}
};
|