Loading

21.7 杂题

杂题分享,这里是题单,下文是部分题的题解。


xmas contest 2018 G - Good Game

https://atcoder.jp/contests/xmascon18/tasks/xmascon18_g

题目日文,所以理解题意可能有点困难。但是因为我是鸽子,所以有空的时候再来补一下题意吧。

你发现这是一个双人博弈的问题,有一个常见的套路就是走模仿棋。考虑这道题能不能这么干。其实是可以的。

如果 \(n,m\) 有一个是偶数的话我们选后手直接以中间为对称轴走模仿棋即可。容易发现如果形成了一个正方形的话对手一定会先形成。

考虑都为奇数的情况怎么办?大胆猜测这时大概先手必胜(要不然题目就不会要你选先后手了),于是你选先手。容易发现一种获胜条件即苟到最后。我们尝试让横着连续的两格不存在相同。于是我们把两格分成一组,走模仿棋。但这显然不对,于是我们尝试把上下错开,每行选一个特殊格式落单的,可以选 \(1\) 好或者 \(m\) 号。然后剩下的两个两个一组,连续两行再错开。走单格也走模仿棋,最后没得走的一定是后手。

void p(int x, int y) {
	print(x - 1); print(y - 1);
	pc('\n'); fflush(stdout);
}
void solve() {
	int n, m, x, y;
	read(n); read(m);
	if (n % 2 == 0) {
		puts("Second");
		fflush(stdout);
		while (1) {
			read(x); read(y);
			if (x == -1 && y == -1) break;// win 
			x++; y++;
			p(n - x + 1, y); 
		}
	} else if (m % 2 == 0) {
		puts("Second");
		fflush(stdout);
		while (1) {
			read(x); read(y);
			if (x == -1 && y == -1) break;// win 
			x++; y++;
			p(x, m - y + 1);
		}
	} else {
		puts("First");
		fflush(stdout);
		p(1, m);
		while (1) {
			read(x); read(y);
			if (x == -1 && y == -1) break;// win 
			x++; y++;
			if (y == 1 && x % 2 == 0) {
				p(x + 1, m);
			} else if (y == m && x % 2 == 1) {
				p(x - 1, 1);
			} else {
				if (x % 2 == 1) {
					if (y % 2 == 1) p(x, y + 1);
					else p(x, y - 1);
				} else {
					if (y % 2 == 0) p(x, y + 1);
					else p(x, y - 1);
				}
			}
		}
	}
}

CF392E

https://codeforces.com/problemset/problem/392/E

区间 dp 经典题。

\(dp[i]\) 代表前 \(i\) 个的答案
\(f[j+1,i]\) 代表删完 \([j+1,i]\) 的最大价值
\(dp[i]=max(dp[i-1],dp[j]+f[j+1,i])\)

\(f[i,j]\) 区间 dp 求
转移拆成两部分 \(f[i,j]=max(f[i,k]+f[k+1,j])\)
还有可能一起删 \(i,j\) ,考虑 \(u[i,j]\) 代表删成上升,\(d[i,j]\) 删成下降, 然后枚举即可。

然后 \(u[i,j]\)\(d[i,j]\) 又都可以区间 dp 求。比如求 \(u\),枚举一个 \(w[k]=w[i]+1\),然后就可以用 \(f[j+1,k-1]+u[k,j]\) 来转移。

#include <bits/stdc++.h>
using namespace std;
template <typename T>
void read(T &x) {
	T flag = 1;
	char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) if (ch == '-') flag = -1;
	for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
	x *= flag;
}
const int maxn = 405;
int n;
int v[maxn], w[maxn];
int u[maxn][maxn], d[maxn][maxn], f[maxn][maxn], dp[maxn];
int ans;
void cmax(int &x, int y) { x = max(x, y); }
int main() {
	memset(f, 0xcf, sizeof(f));
	memset(d, 0xcf, sizeof(f));
	memset(u, 0xcf, sizeof(f));
	read(n);
	for (int i = 1; i <= n; i++) read(v[i]);
	for (int i = 1; i <= n; i++) read(w[i]);
	for (int i = 1; i <= n; i++) f[i][i] = v[1], u[i][i] = d[i][i] = 0;
	for (int len = 2; len <= n; len++) {
		for (int i = 1; i + len - 1 <= n; i++) {
			int j = i + len - 1;
			for (int k = i; k < j - 1; k++) {
				if (w[k] + 1 == w[j]) {
					cmax(u[i][j], u[i][k] + f[k + 1][j - 1]);
				}
			}
			if (w[j - 1] + 1 == w[j]) cmax(u[i][j], u[i][j - 1]);
			for (int k = i + 2; k <= j; k++) {
				if (w[k] + 1 == w[i]) {
					cmax(d[i][j], d[k][j] + f[i + 1][k - 1]);
				}
			}
			if (w[i + 1] + 1 == w[i]) cmax(d[i][j], d[i + 1][j]);
//			cout << i << " " << j << "\n";
			for (int k = i; k < j; k++) {
				cmax(f[i][j], f[i][k] + f[k + 1][j]);
			}
			if (w[j] > w[i] && w[j] - w[i] + 1 <= n) cmax(f[i][j], u[i][j] + v[w[j] - w[i] + 1]);
			if (w[j] < w[i] && w[i] - w[j] + 1 <= n) cmax(f[i][j], d[i][j] + v[w[i] - w[j] + 1]);
			for (int k = i + 1; k < j; k++) {
				if (2 * w[k] - w[i] - w[j] > 0 && 2 * w[k] - w[i] - w[j] + 1 <= n) cmax(f[i][j], u[i][k] + d[k][j] + v[2 * w[k] - w[i] - w[j] + 1]);
			}
//			cout << i << " " << j << "\n";
//			cout << f[i][j] << "\n";
		} 
	}
	for (int i = 1; i <= n; i++) {
		dp[i] = dp[i - 1];
		for (int j = 0; j < i; j++) {
			dp[i] = max(dp[i], dp[j] + f[j + 1][i]);
		}
	}
	printf("%d\n", dp[n]);
	return 0;
}// ntf txdy

CF1218G

这个图是的三部?图,于是我们不妨假设令最后每个点的权值 \(d_u\equiv col_u\pmod 3\)。先搞出一个 dfs 树(貌似是套路),然后发现可以通过父亲边调整,每个点都可以满足(除了根)。设 \(m=col_{root}-d_{root}\),那么如果我们可以找到包含根的一个奇环,我们不断在上面 \(-m,+m\),与根相连的两条边都是 \(-m\),这样相当于 \(+m\bmod 3\)。而其他点都不变,也就满足了条件。于是如果有奇环选一个点当根即可。否则这个图一定是个二分图。还是用同样的方法,但是我们给点重新染色,但是注意这里还是在模 \(3\) 的意义下。不妨令根的颜色为 \(0\),这样只有当最后算出情况是 \(1\) 的时候才会出错。可我们发现 \(2\) 这种点权还没用,这时我们不妨选两条边给其加 \(1\),容易发现这时并没有什么问题。如果根没有连出去两条,你当然可以选择度数大于 \(2\) 的点当根,但我们仔细思考一下,这个点权是在模意义下的,所以这两个相邻的点的点权一定不会相同。

代码有点小小的大问题,但是大体思路没错(应该只是细节出错,如果有人帮忙找到请提出一下,谢谢)。不知道为什么会 re。调了一下边的顺序就过了?cf 的数据不太行啊。

#include <bits/stdc++.h>
#define pb push_back
#define pf push_front
#define mp make_pair
using namespace std;
template <typename T>
void read(T &x) {
	T flag = 1;
	char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) if (ch == '-') flag = -1;
	for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
	x *= flag;
}
const int maxn = 100005;
struct e{
	int u, v, val;
}ans[maxn];
int n, m;
char s[maxn];
int col[maxn], f[maxn], val[maxn], d[maxn];
vector<pair<int, int>> vec[maxn], t[maxn];
bool vis[maxn];
int tmp[maxn];
int flag, End;
int pre[maxn], num[maxn];
void print(int x, int y, int z) {
	printf("%d %d %d\n", x, y, z);
}
void dfs(int x, int fa, int id) {
	f[x] = fa;
	if (fa) t[x].pb(mp(fa, id));
//	cout << x << " " << fa << "\n";
	for (int i = 0; i < (int)vec[x].size(); i++) {
		int y = vec[x][i].first;
		if (f[y] > 0 || y == flag) continue;
		t[x].pb(mp(y, vec[x][i].second));
		dfs(y, x, vec[x][i].second);
	}
}
void get_ans(int x) {
	for (int i = 0; i < (int)t[x].size(); i++) {
		int y = t[x][i].first;
		if (y == f[x]) continue;
		get_ans(y);
		int m = col[y] - d[y];
		ans[t[x][i].second].val = m;
		d[y] = d[y] + m;
		d[x] = d[x] + m; 
	}
}
void get_ans2(int x) {
	for (int i = 0; i < (int)t[x].size(); i++) {
		int y = t[x][i].first;
		if (y == f[x]) continue;
		get_ans2(y);
		int m = tmp[y] - d[y];
		ans[t[x][i].second].val = m;
		d[y] = d[y] + m;
		d[x] = d[x] + m;
	}
}
void check(int x) {
//	if (flag) return;
	for (int i = 0; i < (int)vec[x].size(); i++) {
		int y = vec[x][i].first;
		if (tmp[y] == -1) tmp[y] = tmp[x] ^ 1, check(y);
		else {
			if (tmp[y] == tmp[x]) {
				flag = x;// 不是二分图 
//				return;
			}
		}
	}
}
//void jjj(int x) {
//	for (int i = 0)
//}
void yyy(int x) {
	for (int i =0; i < (int)vec[x].size(); i++) {
		int y = vec[x][i].first;
		if (tmp[y] != -1) continue;
		tmp[y] = tmp[x] ^ 1;
		yyy(y);
	}
}
int cnt[maxn];
void output() {
	for (int i = 1; i <= m; i++) {
		ans[i].val = (ans[i].val % 3 + 3) % 3;
		if (ans[i].val == 0) ans[i].val = 3;
		print(ans[i].u, ans[i].v, ans[i].val);
		cnt[ans[i].u] += ans[i].val;
		cnt[ans[i].v] += ans[i].val;
	}
//	for (int i = 0; i < n; i++) cout << i << " " << (cnt[i]) << "\n";
//	 cout <<"\n"; 
//	for (int i = 1; i <= m; i++) {
//		if (cnt[ans[i].u] == cnt[ans[i].v]) cout << i << "\n";
//	}
}
void jjj(int x) {
	for (int i = 0; i < (int)vec[x].size(); i++) {
		int y = vec[x][i].first;
		if (tmp[y] == -1) tmp[y] = tmp[x] ^ 1, pre[y] = x, num[y] = vec[x][i].second, jjj(y);
		else {
			if (y == flag && tmp[x] == tmp[flag]) {
				End = x;
			}
		}
	}
}
void work() {
	tmp[1] = 0;
	check(1);
//	puts("check");
	if (flag) {// 不是二分图  
//		puts("");
//flag = 1;
//		cout << flag << "\n";
		dfs(flag, 0, 0);
//		for (int i = 1; i <= n; i++) printf("%d ", f[i]);
//		puts("");
//		cout << flag << "\n";
		get_ans(flag);
//		for (int i = 1; i <= n; i++) cout << d[i] << " ";
//		cout << endl;
//		puts("check");
//		if (col[flag] == d[flag] % 3) {
//			output();
//		} else {
//			puts("check");
			int m = (col[flag] - d[flag]);
			memset(tmp, -1, sizeof(tmp));
			tmp[flag] = 0;
			jjj(flag);
//			cout << flag << "\n";
//		cout << m << "\n";
//			for (int i = 1; i <= n; i++) cout << pre[i] << " ";
//			return;
			for (int i = End; i != flag; i = pre[i]) {
//				cout <<  i << " ";
				ans[num[i]].val += m;
				m = -m;
			}// cout << endl;
			for (int i = 0; i < (int)vec[End].size(); i++) {
				if (vec[End][i].first == flag) ans[vec[End][i].second].val += m << 1;
			}
			output();
//		}
	} else {
		
		flag = 1;
		memset(tmp, -1, sizeof(tmp));
		tmp[1] = 0;
		
		yyy(flag);
		dfs(flag, 0, 0);
		get_ans2(flag);
//		for (int i = 1; i <= n; i++) cout << d[i] << " ";
//		cout << endl;
		if ((d[1] % 3 + 3) % 3 == 1 && vec[1].size() > 1) {
			ans[vec[1][1].second].val++; ans[vec[1][2].second].val++;
//			puts("check");
		}
		
			output();
	}
}
int main() {
	memset(tmp, -1, sizeof(tmp));
	read(n); read(m);
	scanf("%s", s + 1);
	for (int i = 1; i <= n; i++) {
		col[i] = s[i] - 'X';
	}
	for (int i = 1; i <= m; i++) {
		int u, v;
		read(u); read(v);
		ans[i].u = u; ans[i].v = v;
		u++; v++;
		vec[u].pb(mp(v, i));
		vec[v].pb(mp(u, i));
	}
	for (int i = 1; i <= n; i++) reverse(vec[i].begin(), vec[i].end());
	work();
	return 0;
}

arc 075d

小清新输入输出题

设最后的数 \(n\) 的位数为 \(m\)\(a_i=n/10^i \bmod 10\)。反转后的答案是 \(\sum\limits_{i=0}^{\lceil\frac{m}{2}\rceil}(10^{m-i}-10^i)(a_{i}-a_{m-i})=\sum\limits_{i=0}^{\lceil\frac{m}{2}\rceil}\overline{99\dots900\dots0}\cdot(a_{i}-a_{m-i})\),有 \(i\)\(0\)\(m-2i\)\(9\)。于是我们发现这一定是一个 \(9\) 的倍数,我们不妨直接除以一个 \(9\)

\[\sum\limits_{i=0}^{\lceil\frac{m}{2}\rceil}\overline{11\dots100\dots0}\cdot(a_{i}-a_{m-i}) \]

然后我们又发现个位数之和 \(a_{m-i}-a_i\) 有关。于是个位就确定了而且只有两种可能,暴力枚举即可。而后面都有公因数 \(10\),一起除掉即可。如果最后都算完发现数变成了 \(0\) 就合法。复杂度 \(O(m2^{m})\)\(m\) 是枚举的位数。 根据尝试知道 \(n\) 是个 longlong 范围内的数,所以 \(m\) 枚举到 \(18\) 即可。

代码也非常小清新

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
ll D, ans, m, pw[30], a, b;
void dfs(int i, ll num, ll now) {
	if (i > m / 2) { ans += num * (now == 0); return; }
	a = (now % 10 + 10) % 10, b = m - 2 * i;
	if ((now - (a * pw[b])) % 10 == 0)
		dfs(i + 1, i == 0 ? num * (10 - a - 1) : num * (10 - a), (now - (a * pw[m - 2 * i])) / 10);
	if ((now - (a - 10) * pw[b]) % 10 == 0)
		dfs(i + 1, num * a, (now - (a - 10) * pw[m - 2 * i]) / 10);
}
int main() {
	for (int i = 1; i <= 18; i++) pw[i] = pw[i - 1] * 10 + 1;
	cin >> D;
	if (D % 9) { puts("0"); return 0; }
	for (m = 1; m <= 18; m++) dfs(0, 1, D / 9);
	cout << ans << endl;
	return 0;
}
posted @ 2021-07-08 22:55  Semsue  阅读(5)  评论(0编辑  收藏  举报
Title