E - Salvage Robots

动多个机器人可以看作动一个出口。
出口回收范围一定是一个矩形,任存在的机器人范围也是矩形。
dp状态存出口矩形四个方向的延伸量,每次某方向上扩展一格。

kk

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
char s[N][N];
int sh[N][N], sl[N][N], val[N][N];
short dp[105][105][105][105];

int main() {
	int n, m; scanf("%d%d", &n, &m);
	int ex, ey;
	for(int i = 1; i <= n; i++) {
		scanf("%s", s[i] + 1);
		for(int j = 1; j <= m; j++) {
			if(s[i][j] == 'E') {ex = i; ey = j;}
			else if(s[i][j] == 'o') {sh[i][j] = sl[i][j] = 1;}
			sh[i][j] += sh[i][j - 1]; sl[i][j] += sl[i - 1][j];
		}
	}
	int ans = 0;
	for(int l = 0; l <= ey - 1; l++) {
		for(int r = 0; r <= m - ey; r++) {
			for(int u = 0; u <= ex - 1; u++) {
				for(int d = 0; d <= n - ex; d++) {
					ans = max(ans, (int)dp[l][r][u][d]);
					if(l + r < ey - 1) {dp[l + 1][r][u][d] = max((int)dp[l + 1][r][u][d], dp[l][r][u][d] + sl[min(ex + d, n - u)][ey - l - 1] - sl[max(d, ex - u - 1)][ey - l - 1]);}
					if(l + r < m - ey) {dp[l][r + 1][u][d] = max((int)dp[l][r + 1][u][d], dp[l][r][u][d] + sl[min(ex + d, n - u)][ey + r + 1] - sl[max(d, ex - u - 1)][ey + r + 1]);}
					if(u + d < ex - 1) {dp[l][r][u + 1][d] = max((int)dp[l][r][u + 1][d], dp[l][r][u][d] + sh[ex - u - 1][min(ey + r, m - l)] - sh[ex - u - 1][max(ey - l - 1, r)]);}
					if(u + d < n - ex) {dp[l][r][u][d + 1] = max((int)dp[l][r][u][d + 1], dp[l][r][u][d] + sh[ex + d + 1][min(ey + r, m - l)] - sh[ex + d + 1][max(ey - l - 1, r)]);}
				}
			}
		}
	}
	printf("%d", ans);
	return 0;
}

F - Namori

思路有点繁杂
首先是模型转换,树是二分图可黑白染色,转换为相邻不同黑白交换问题,或者是把黑看作棋子白看作空位的移棋子问题。

  • 树,\(a(i)\) 表示子树内的黑白差,\(i\)\(fa(i)\) 之间至少交换 \(|a(i)|\) 次,而发现可以取到这个下界(不会证),答案为 \(\sum|a(i)|\)
  • 偶环(可以二分图),设环上转移量为 \(x\) 数列,可以对每个点根据最终黑白差\(=0\)列方程,具体看上面博客,可以通过中位数得到最值,然后答案就是子树内部的 \(\sum|a(i)|\)\(x\) 数列和。
  • 奇环:非树边的作用是可以将两端同时反色,而这样就能改变黑白点数,所以操作次数固定为\(\frac{a(rt)}{2}\), 这样可以直接改环上最深的那个点的\(a\)值。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
int a[N], head[N], to[N], nxt[N], ecnt, fa[N], b[N], lst[N], tot;
void add_edge(int u, int v) {nxt[++ecnt] = head[u]; to[ecnt] = v; head[u] = ecnt;}
int g_fa(int u) {return fa[u] == u ? u : fa[u] = g_fa(fa[u]);}

void dfs(int u, int op) {
	a[u] = op;
	for(int i = head[u]; i; i = nxt[i]) {
		int v = to[i]; if(v == lst[u]) continue;
		lst[v] = u; dfs(v, -op); a[u] += a[v];
	}
}

int main() {
	int n, m, rt = 1, ed; scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++) fa[i] = i;
	for(int i = 1; i <= m; i++) {
		int u, v; scanf("%d%d", &u, &v);
		int x(g_fa(u)), y(g_fa(v));
		if(x == y) {rt = u; ed = v;}
		else {fa[x] = y; add_edge(u, v), add_edge(v, u);}
	}
//	printf("!rt=%d ed=%d\n", rt, ed);
	dfs(rt, 1);
	if(n == m) {
		for(int x = ed; x; x = lst[x]) {b[++tot] = a[x];}
		//the times of (ed->rt) are add on a[rt]
		if(tot & 1) {
			if(a[rt] & 1) {puts("-1"); return 0;}
			for(int x = ed; x; x = lst[x]) {a[x] -= a[rt] >> 1;}
		}
		else {
			if(a[rt]) {puts("-1"); return 0;} 
			sort(b + 1, b + 1 + tot);
			for(int x = ed; x; x = lst[x]) {a[x] -= b[tot >> 1];}
		}
	}
	else if(a[rt]) {puts("-1"); return 0;}
	ll ans = 0;
	for(int i = 1; i <= n; i++) {ans += abs(a[i]);}
	printf("%lld", ans);
	return 0;
}