AtCoder Regular Contest 078D: Fennec VS. Snuke

http://arc078.contest.atcoder.jp/tasks/arc078_b

解法自体は簡単だけど、バグらせたので。
こういう境界でバグらせやすい問題は[a b)の区間で考えるのがよさそう。
[a (a+b)/2)にすると半分、またはそれより1小さくなり、
[a (a+b+1)/2)にすると半分、またはそれより1大きくなる。

またwhileはカッコ内をループ内で主に注目する変数の存在条件にして、1進む処理をwhile文の一番下でやるのが良いだろう。
こうすればfor文と全く同じ形になる。
nextとnowを持って何かをするときはnextについて処理を行うことに注意しよう。dp的に考えたら当たり前のことだが。

vector<int> dep, tz;
vector<vector<int>> G;
vector<vector<int>> child;
vector<int> par;

int dfs(int v, int de = 0, int p = -1) {
	par[v] = p;
	dep[v] = de;
	ll res = 1;
	rep(i, 0, sz(G[v])) {
		int u = G[v][i];
		if(p == u) continue;
		child[v].push_back(u);
		res += dfs(u, de + 1, v);
	}
	return tz[v] = res;
}

void solve() {
	int N; cin >> N;
	dep.resize(N); tz.resize(N);
	G.resize(N);
	child.resize(N); par.resize(N);
	rep(i, 0, N - 1) {
		int a, b; cin >> a >> b; a--; b--;
		G[a].push_back(b);
		G[b].push_back(a);
	}
	dfs(0);
	int v = N - 1;
	int f = (dep[N - 1] + 2) / 2, g = (dep[N - 1] + 1) / 2;
	rep(i, 0, sz(child[N - 1])) {
		int u = child[N - 1][i];
		g += tz[u];
	}
	while(par[v] != -1) {
		int uv = par[v];
		ll tmp = 0;
		rep(i, 0, sz(child[uv])) {
			int u = child[uv][i];
			if(u == v) continue;
			tmp += tz[u];
		}
		if(dep[uv] > (dep[N - 1] + 2) / 2 - 1) g += tmp;
		else f += tmp;
		v = uv;
	}
	/*
	rep(i, 0, N) debug(child[i]);
	debug(dep); debug(tz);
	debug(f, g);
	*/
	if(f > g) cout << "Fennec" << "\n";
	else cout << "Snuke" << "\n";
}

Codeforces Round #423E: Rusty String

http://codeforces.com/contest/827/problem/E

FFTです

k periodである条件は|i-j|%k=0かつS[i]とS[j]がそれぞれVとKであるi,jが存在しないことである。
そこで、S[i]='V'ならばA[i]=1でありそれ以外の要素は0である配列Aと、S[N-i-j]='V'ならばB[i]=1であり、それ以外の要素は0である配列Bをとる。
AとBをFFTして得られた配列Cのi番目の要素C[i]について
C[i]>0 <=> A[j]=1かつB[i-j]=1 <=> S[j]='V'かつS[n-i+j]='K'である。
よってk periodであるためには
(n-i+j)-j=n-iがkで割り切れるすべてのiについてC[i]=0が成立すれば良い。

計算量はfftでO(NlogN)、最後のC[i]=0を確かめるところでΣ(N/k)=O(NlogN)なので間に合う。

struct FFT {
	void fft(vector<comp>& v, bool rev = false) { //v.size() == (1 << x)
		int n = sz(v), i, j;
		for(i = 0, j = 1; j < n - 1; j++) {
			for(int k = n >> 1; k > (i ^= k); k /= 2);
			if(i > j) swap(v[i], v[j]);
		}
		for(int m = 2; m <= n; m *= 2) {
			comp wr = polar(1.0, (rev? -1 : 1) * 2.0 * PI / m);
			for(int i = 0; i < n; i += m) {
				comp w(1, 0);
				rep(j, 0, m / 2) {
					comp f0 = v[i + j], f1 = w * v[i + j + m / 2];
					v[i + j] = f0 + f1;
					v[i + j + m / 2] = f0 - f1;
					w *= wr;
				}
			}
		}
		if(rev) rep(i, 0, n) v[i] *= 1.0 / n;
	}
	vector<int> get(const vector<int>& A, const vector<int>& B) {
		int n = 1;
		while(n < sz(A) + sz(B)) n *= 2;
		vector<comp> a(n), b(n);
		rep(i, 0, sz(A)) a[i] = comp(A[i], 0);
		rep(i, 0, sz(B)) b[i] = comp(B[i], 0);
		fft(a); fft(b);
		rep(i, 0, n) a[i] *= b[i];
		fft(a, true);
		vector<int> res(n);
		rep(i, 0, n) res[i] = (int)(a[i].real() + 0.5);
		return res;
	}
};

FFT F;

void solve() {
	int Q;
	cin >> Q;
	while(Q--) {
		int N; cin >> N;
		string str; cin >> str;
		vector<int> A(N, 0), B(N, 0);
		rep(i, 0, N) {
			if(str[i] == 'V') A[i] = 1;
			else if(str[i] == 'K') B[N - 1 - i] = 1;
		}
		vector<int> v = F.get(A, B);
		//debug(v);
		vector<int> ans;
		rep(k, 1, N) {
			bool ok = true;
			for(int m = 0; N / k * k + N - 1 - k * m >= 0; m++) {
				if(v[N / k * k + N - 1 - k * m]) {
					ok = false;
					break;
				}
			}
			if(ok) ans.push_back(k);
		}
		ans.push_back(N);
		cout << sz(ans) << "\n";
		rep(i, 0, sz(ans)) cout << ans[i] << " ";
		cout << "\n";
	}
}

fft自体ははライブラリ化すれば問題ない。
帰着させるところが難しい。
少しずつずれたものを考える時に有効?

Codeforces Round #423D: Best Edge Weight

初HL分解です。

http://codeforces.com/contest/827/problem/D

解法自体は簡単で、ある辺について、それを含むループの中の辺での最大cost-1が答えとなる。
これはMSTを作ってHL分解すればできる。

snukeさんのコードを参考にした。
HL以外のところでめちゃくちゃバグらせた。

struct UF {
	vector<int> par, ran;
	void init(int n) {
		par.resize(n); ran.resize(n);
		for(int i = 0; i < n; i++) {
			par[i] = i;
			ran[i] = 0;
		}
	}
	UF(int mx = 0) { init(mx); }

	int find(int x) {
		if(par[x] == x) return x;
		else return par[x] = find(par[x]);
	}
	void unite(int x, int y) {
		x = find(x);
		y = find(y);
		if(x == y) return;
		if(ran[x] < ran[y]) {
			par[x] = y;
		}
		else {
			par[y] = x;
			if(ran[x] == ran[y]) ran[x]++;
		}
	}
	bool same(int x, int y) { return find(x) == find(y); }
};


struct segtree{
	int n; vi sum, lazy;
	void init(int mx) {
		n = 1;
		while(n < mx) n *= 2;
		sum = vi(n * 2, -inf);
		lazy = vi(n * 2, -inf);
	}
	segtree(int mx = 0) { init(mx); }
	inline void lazy_eval(int k, int l, int r) {
		if(lazy[k] != -inf) {
			MAX(sum[k * 2 + 1], lazy[k]);//if range_min, erase (m - l)
			MAX(lazy[k * 2 + 1], lazy[k]);
			MAX(sum[k * 2 + 2], lazy[k]);//here too
			MAX(lazy[k * 2 + 2], lazy[k]);
			lazy[k] = -inf;
		} 
	}
	void app(int a, int b, ll s, int k, int l, int r) {
		if(b <= l || r <= a) return;
		if(a <= l && r <= b) {
			MAX(sum[k], s); //here too
			MAX(lazy[k], s);
		}
		else {
			lazy_eval(k, l, r);
			app(a, b, s, k * 2 + 1, l, (l + r) / 2);
			app(a, b, s, k * 2 + 2, (l + r) / 2, r);
			sum[k] = max(sum[k * 2 + 1], sum[k * 2 + 2]);
		}
	}
	ll ga(int a, int b, int k, int l, int r) {
		if(b <= l || r <= a) return -inf;
		if(a <= l && r <= b) return sum[k];
		else {
			lazy_eval(k, l, r);
			ll lv = ga(a, b, k * 2 + 1, l, (l + r) / 2);
			ll rv = ga(a, b, k * 2 + 2, (l + r) / 2, r);
			return max(lv, rv);
		}
	}
	ll get(int a, int b) { return ga(a, b, 0, 0, n); } //[a, b)
	void update(int a, int b, ll s) { app(a, b, s, 0, 0, n); } //[a, b)
};

struct HL {
	int n;
	vector<vi> to, cost, D;//D:group
	vi vcost, vd, vk, pv, dep;//vcost:edge cost,vd:D at,vk:num from parent,pv:group par,dep:depth
	vector<segtree> S; //change 
	vector<segtree> S2; //change
	int root;

	HL(int mx = 0): n(mx), to(mx), cost(mx), vcost(mx), vd(mx), vk(mx), dep(mx) {}
	void add(int a, int b, int c = 1) {
		to[a].pb(b); cost[a].pb(c);
		to[b].pb(a); cost[b].pb(c);
	}

	int dfs(int v, int depth = 0, int p = -1) {
		dep[v] = depth;
		vector<pi> rs;
		rep(i, 0, sz(to[v])) {
			int u = to[v][i];
			if(u == p) continue;
			vcost[u] = cost[v][i];
			rs.pb(pi(dfs(u, depth + 1, v), u));
		}
		if(sz(rs)) {
			sort(all(rs));
			vd[v] = vd[rs[0].sec]; pv[vd[v]] = p;
			D[vd[v]].pb(v); vk[v] = -sz(D[vd[v]]);
			return rs[0].fst - (sz(rs) != 1 && rs[0].fst == rs[1].fst);
		}
		else {
			vd[v] = sz(D); vk[v] = -1; D.pb(vi(1, v)); pv.pb(p);
			return 1;
		}
	}
	void init(int v = 0) {
		root = v;
		dfs(root);
		rep(i, 0, sz(D)) reverse(all(D[i]));
		rep(i, 0, sz(vk)) vk[i] += sz(D[vd[i]]);
		rep(i, 0, sz(D)) {
			S.pb(segtree(sz(D[i])));      
			S2.pb(segtree(sz(D[i])));//change
			rep(j, 0, sz(D[i])) S[i].update(j, j + 1, vcost[D[i][j]]); //change
		}
	}
	int lca(int a, int b) {
		int p = vd[a]; vi ap(1, p);
		while(pv[p] != -1) ap.pb(p = vd[pv[p]]);
		reverse(all(ap)); ap.pb(-1);
		p = vd[b]; vi bp(1, p);
		while(pv[p] != -1) bp.pb(p = vd[pv[p]]);
		reverse(all(bp)); bp.pb(-2);
		int pi = 1; while(ap[pi] == bp[pi]) pi++;
		p = ap[pi - 1];
		int ai = vd[a] == p ? vk[a] : vk[pv[ap[pi]]];
		int bi = vd[b] == p ? vk[b] : vk[pv[bp[pi]]];
		return D[p][min(ai, bi)];
	}

	//////////////////////////////////////////////////////////////////////////////from here change
	ll ga(int par, int v) {
		ll res = -inf;
		while(true) {
			int nd = vd[v], nk = vk[v];
			if(nd == vd[par]) {
				return max(res, S[nd].get(vk[par] + 1, nk + 1));
			}
			else {
				MAX(res, S[nd].get(0, nk + 1));
			}
			v = pv[nd];
		}
	}
	void app(int par, int v, ll x) {
		while(true) {
			int nd = vd[v], nk = vk[v];
			if(nd == vd[par]) {
				S2[nd].update(vk[par] + 1, nk + 1, -x);
				return;
			}
			else {
				S2[nd].update(0, nk + 1, -x);
			}
			v = pv[nd];
		}
	}
	int query(int a, int b, ll x) {
		int par = lca(a, b);
		ll res =ga(par, a);
		MAX(res, ga(par, b));
		app(par, a, max(res, x));
		app(par, b, max(res, x));
		return res;
	}
	int get(int a, int b) {
		if(dep[a] < dep[b]) a = b;
		return -S2[vd[a]].get(vk[a], vk[a] + 1);
	}
};

struct edge {
	int a, b, c, i;
	bool operator<(const edge& e) {
		return c < e.c;
	}
};

int N, M;
edge es[MAX_N];
int ans[MAX_N];

void solve() {
	cin >> N >> M;
	rep(i, 0, M) {
		int a, b, c;
		cin >> a >> b >> c; a--; b--;
		es[i] = (edge){a, b, c, i};
	}
	UF uf(N);
	sort(es, es + M);
	HL g(N);
	rep(i, 0, M) {
		int a = es[i].a, b = es[i].b, c = es[i].c, at = es[i].i;
		if(!uf.same(a, b)) {
			uf.unite(a, b);
			ans[at] = -1;
			g.add(a, b, c);
		}
	}
	g.init();
	rep(i, 0, M) {
		int a = es[i].a, b = es[i].b, c = es[i].c, at = es[i].i;
		if(ans[at] == -1) continue;
		ans[at] = g.query(a, b, c);
	}
	rep(i, 0, M) {
		int a = es[i].a, b = es[i].b, at = es[i].i;
		if(ans[at] != -1) continue;
		ans[at] = g.get(a, b);
	}
	rep(i, 0, M) {
		if(ans[i] == inf) {
			cout << -1 << "\n";
		}
		else {
			cout << ans[i] - 1 << "\n";
		}
	}
}

UF+segtree+LCA+HLdecですごいエグイ感じになってて楽しい。
doublingのLCA+sparse tableっぽいものでもできる。そっちも実装したいね。

Codeforces Round #421B: Mister B and PR Shifts

http://codeforces.com/contest/819/problem/B

傾きと切片を持って適当にやればいい…がvectorの形で保持したらTLEぎりぎりになってしまった。
累積和でやりましょう。
あとmodの条件もいろいろ勘違いして結構バグらせた。
modは1違うだけで値が劇的に変わったりするので、油断しない。

int N;
int A[MAX_N];
vector<int> M[MAX_N * 2], B[MAX_N * 2];
ll ans[MAX_N * 2];

void solve() {
	cin >> N;
	rep(i, 0, N) {
		cin >> A[i];
		M[N - i].push_back(-1);
		B[N - i].push_back(A[i] - 1);
		M[N + A[i] - 1 - i].push_back(2);
		B[N + A[i] - 1 - i].push_back(0);
		M[2 * N - i].push_back(-1);
		B[2 * N - i].push_back(-(N - A[i] + 1));
	}
	ll res = 0, m = 0;
	rep(i, 1, 2 * N + 1) {
		res += m;
		rep(j, 0, (int)B[i].size()) {
			res += B[i][j];
			m += M[i][j];
		}
		ans[i] = res;
	}
	ll tmp = inf;
	int at = -1;
	rep(i, 1, N + 1) { 
		ll t = ans[i] + ans[i + N];
		if(tmp > t) {
			tmp = t;
			at = i;
		}
	}
	cout << tmp << " " << at % N << "\n";
}

Codeforces Round #419C: Karen and Supermarket

http://codeforces.com/contest/815/problem/C

3乗っぽいけど2乗な木dp。
5000*5000*2のlong longの配列持ったらMLEした。
さらに初期化を適当にやりすぎてO(N^3)になってしまった。
直してAC。

int N, B;
vector<int> G[MAX_N];
int dp[MAX_N][MAX_N][2];
int dp2[MAX_N][MAX_N][2];

int C[MAX_N], D[MAX_N];
int E[MAX_N];

int loop1(int at) {
	int res = 1;
	int S = (int)G[at].size();
	rep(i, 0, S) {
		int n = G[at][i];
		res += loop1(n);
	}
	return E[at] = res;
}

void loop2(int at) {

	int S = (int)G[at].size();
	rep(i, 0, S) {
		loop2(G[at][i]);
	}
	
	int M = 0;
	rep(j, 0, E[at] + 1) {
		dp2[0][j][0] = inf;
		dp2[0][j][1] = inf;
	}
	dp2[0][0][0] = 0;
	dp2[0][0][1] = 0;

	rep(i, 0, S) {
		int n = G[at][i];
		rep(j, 0, M + E[n] + 2) {
			dp2[i + 1][j][0] = inf;
			dp2[i + 1][j][1] = inf;
		}
		rep(j, 0, M + 1) {
			rep(k, 0, E[n] + 1) {
				MIN(dp2[i + 1][j + k][0], dp[n][k][0] + dp2[i][j][0]);
				MIN(dp2[i + 1][j + k][1], dp[n][k][1] + dp2[i][j][1]);
				MIN(dp2[i + 1][j + k][0], inf);
				MIN(dp2[i + 1][j + k][1], inf);
			}
		}
		M += E[n];
	}
	dp[at][0][0] = 0;
	dp[at][0][1] = 0;
	rep(j, 1, E[at] + 1) {
		dp[at][j][0] = min(dp2[S][j - 1][0] + C[at], dp2[S][j][0]);
		dp[at][j][1] = min(dp2[S][j - 1][1] + D[at], dp2[S][j][0]);
		MIN(dp[at][j][0], inf);
		MIN(dp[at][j][1], inf);
	}
}

void solve() {
	cin >> N >> B;
	rep(i, 0, N) {
		cin >> C[i] >> D[i];
		D[i] = C[i] - D[i];
		if(i) {
			int a; cin >> a; a--;
			G[a].push_back(i);
		}
	}
	loop1(0);
	loop2(0);

	rep(i, 0, N + 1) {
		if(dp[0][i][1] > B) {
			cout << i - 1 << "\n";
			return;
		}
	}

	cout << N << "\n";
}

Codeforces Round #423B: High Load

http://codeforces.com/contest/827/problem/B

最初にrootを一つ決めて、あとはkコずつrootからまんべんなくつけていけば、木の高さが最小、かつその時の一番rootから離れているedgeの個数も最小になるのでこれが求めるグラフとなる。

int N, K;

void solve() {
	cin >> N >> K;
	if((N - K - 1) % K == 0) cout << (N - K - 1) / K * 2 + 2 << "\n";
	else if((N - K - 1) % K == 1) cout << (N - K - 1) / K * 2 + 3 << "\n";
	else cout << (N - K - 1) / K * 2 + 4 << "\n";
	vector<int> vec;
	rep(i, 0, K) vec.push_back(i + 1);
	int at = K + 1;
	while(at < N) {
		int a = vec[(at - 1) % K];
		cout << a << " " << at << "\n";
		vec[(at - 1) % K] = at;
		at++;
	}
	rep(i, 0, K) {
		cout << vec[i] << " " << N << "\n";
	}
}

こういうのをウニグラフというらしい。

Codeforces Round #423C: DNA Evolution

http://codeforces.com/contest/827/problem/C

e<=10なのでmod eごとにBITを使って処理する。

int n;

ll sum(ll *bit, int i) {
	ll s = 0;
	while(i > 0) {
		s += bit[i];
		i -= i & -i;
	}
	return s;
}

void add(ll *bit, int i, ll x) {
	while(i <= n) {
		bit[i] += x;
		i += i & -i;
	}
}

struct query {
	int a, b;
	vector<int> vec;
};

ostream& operator<< (ostream& out, const query& v) {
	out << "(" << v.a << ", " << v.b << ", " << v.vec <<  ")";
	return out;
}

int N, Q;
ll bit[4][11][100010];
int ans[100010];

int S[100010];
query qr[100010];

void show_seg(int m) {
	rep(i, 0, 4) {
		rep(j, 0, m) {
			vector<int> vec;
			rep(k, 1, n + 1) {
				vec.push_back(sum(bit[i][j], k));
			}
			debug(i, j, vec);
		}
	}
}

int char_to_int(char c) {
	if(c == 'A') return 0;
	if(c == 'T') return 1;
	if(c == 'G') return 2;
	if(c == 'C') return 3;
	return 4;
}

void solve() {
	string str;
	cin >> str;
	N = str.size();
	rep(i, 0, N) S[i] = char_to_int(str[i]);

	cin >> Q;
	memset(ans, -1, sizeof(ans));
	rep(i, 0, Q) {
		int t; cin >> t;
		int a, b; string s;
		if(t == 1) {
			cin >> b >> s; b--;
			a = -1;
		}
		else {
			cin >> a >> b >> s; a--; b--;
			ans[i] = 0;
		}
		vector<int> vec;
		rep(j, 0, (int)s.size()) vec.push_back(char_to_int(s[j]));
		qr[i] = (query){a, b, vec};
	}

	rep(m, 1, 11) {
		memset(bit, 0, sizeof(bit));
		n = N / m + 2;
		vector<int> ts(S, S + N);
		rep(i, 0, m) {
			for(int k = 0; i + k * m < N; k++) {
				int id = i + k * m;
				add(bit[S[id]][i], k + 1, 1);
			}
		}

		rep(i, 0, Q) {
			int a = qr[i].a, b = qr[i].b;
			vector<int> vec = qr[i].vec;
			if(a == -1) {
				int t = vec[0];
				int pt = ts[b];
				add(bit[pt][b % m], b / m + 1, -1);
				add(bit[t][b % m], b / m + 1, 1);
				ts[b] = t;
			}
			else {
				if((int)vec.size() != m) continue;
				rep(j, 0, m) {
					int l, r;
					l = (a - j + m - 1) / m;
					r = (b - j + m) / m;
					int t = vec[(j - a % m + m) % m];
					ans[i] += sum(bit[t][j], r) - sum(bit[t][j], l);
				}
			}
		}
	}
	rep(i, 0, Q) {
		if(ans[i] == -1) continue;
		cout << ans[i] << "\n";
	}
}

添え字は数学で考えればミスらないで済むね。