【题录】Atcoder ARC#105
C.
贪心的发现第一只和第二只应该越近越好。这样确定了一二之间的距离之后,二三之间也是越近越好。于是阶乘枚举牛的顺序,然后贪心求出答案再对答案取max。
#include <bits/stdc++.h> using namespace std; #define maxn 1000000 #define LL long long #define INF 999999999999999LL #define N 10000 int n, m, bits[N], rec[maxn], w[maxn]; int mxw, a[maxn]; LL ans = INF, dis[maxn]; bool mark[maxn]; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } struct bridge { int l, w; }b[maxn]; void pt(int x) { int c[20]; int cnt = 0; for(int i = 0; i <= n; i ++) c[i] = 0; while(x) { c[++ cnt] = x & 1; x >>= 1; } for(int i = n; i > 0; i --) cout << c[i]; cout << endl; } void Pre_dfs(int st, int now, int w_s) { if(now > n) { if(!st) return; int l = -1; for(int i = 1; i <= m; i ++) if(b[i].w < w_s) l = max(l, b[i].l); rec[st] = l; return; } Pre_dfs(st, now + 1, w_s); Pre_dfs(st | bits[now - 1], now + 1, w_s + w[now]); } void Solve() { for(int i = 0; i <= n; i ++) dis[i] = 0; for(int i = 2; i <= n; i ++) { int st = bits[a[i] - 1]; for(int j = i - 1; j > 0; j --) { st |= bits[a[j] - 1]; int ll = rec[st]; dis[i] = max(dis[i], max(0LL, (LL)ll - (dis[i - 1] - dis[j]))); } dis[i] += dis[i - 1]; } ans = min(ans, dis[n]); } void dfs(int now) { if(now > n) { Solve(); return; } for(int i = 1; i <= n; i ++) if(!mark[i]) { a[now] = i; mark[i] = 1; dfs(now + 1); mark[i] = 0; } } main() { n = read(), m = read(); bits[0] = 1; for(int i = 1; i <= n; i ++) bits[i] = bits[i - 1] << 1; for(int i = 1; i <= n; i ++) { w[i] = read(); mxw = max(mxw, w[i]); } for(int i = 1; i <= m; i ++) { b[i].l = read(), b[i].w = read(); if(b[i].w < mxw) { puts("-1"); return 0; } } Pre_dfs(0, 1, 0); dfs(1); printf("%lld\n", ans); return 0; }
D.
最后所有的袋子里的金钱全部被转移到盘子上了之后,此时如果所有的盘子上的金钱数的异或和为0则先手必败,否则先手必胜。
考虑当袋子数为偶数时,先手即后面的先手。此时先手要想办法使得异或和不为0。倘若所有数目的金钱袋子个数均为偶数,则先手必败,因为后手总能够对称操作使得异或和为0。否则先手每次都可以选择当前局面最大的金钱袋子并把它放到一个盘子上,最终这个盘子上的金钱数一定会大于二分之一,异或和不为0,先手必胜。
当袋子数为奇数时,先手要使异或和为0。但无论先手怎么操作,后手都可以把最多的金钱袋子中的钱倒入这个盘子,使得最后的异或和不为0,先手必败。
#include <bits/stdc++.h> using namespace std; #define maxn 1000000 int a[maxn]; map <int, int> Map; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } int main() { int T = read(); while(T --) { int n = read(); int flag = 0; for(int i = 1; i <= n; i ++) a[i] = read(); for(int i = 1; i <= n; i ++) Map[a[i]] ++; for(int i = 1; i <= n; i ++) if(Map[a[i]] & 1) flag ++, Map[a[i]] = 0; if(!(n & 1)) { if(flag) printf("First\n"); else printf("Second\n"); } else { printf("Second\n"); } for(int i = 1; i <= n; i ++) Map[a[i]] = 0; } return 0; }
E.
设在最后的情况中与1相连的点一共有x个,与n相连的点一共有y个。那么一共可以连的边数为 \( \frac{n * (n - 1)}{2} -x * y - m\),若可以连接的边数为偶数则先手败,否则先手胜。因为 x + y = n, 所以如果n为奇数,那么 x,y 必然一奇一偶,最后的胜负与操作无关。
如果n为偶数:假设最开始与1相连的点一共a个,与n相连的点一共b个。如果 a, b 奇偶性不同,那么 a + b 为奇数,剩下的点组成的若干连通块中必然存在有点数为奇数的。通过将这个连通块与 a 和 b 连接可以变成两个均为偶数 / 两个均为奇数(根据先手必胜需要的条件,使之变为对自己有力的条件)。那么这个时候如果后手通过将连通块连接到1和n上使得连通块改变奇偶性,先手也一定可以同样操作再变回到对自己有利的条件(或者连接的是偶数块,这个时候先手可以选择进行无关操作或者继续将偶数块连接到1或n上)。如果后手不改变1,n两个连通块点数的奇偶性,进行“无关”操作,那么我们分析最后一次不能再进行无关操作的时候。此时不与1,n相连的一定是一个偶数的连通块,先手、后手只能将这个连通块随便连到1或n上。然后就确定了x,y,且其奇偶性对先手有利,先手必胜。
如果最开始 a, b 的奇偶性相同,那么由上述分析可知如果先手移动之后1, n 连通块一奇一偶则先手必败。所以先手只能选择连接偶数块或者进行无关操作。操作之后后手面对的局面相同,也只能选择连接偶数块或者进行无关操作。考虑最后的情况,剩下的一定是一个偶数的连通块。这个时候无法改变奇偶性。所以a, b 的奇偶性会与 x, y 的奇偶性相同。
#include <bits/stdc++.h> using namespace std; #define maxn 1000000 #define int long long int n, m, mark[maxn]; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } struct edge { int cnp = 1, head[maxn], last[maxn], to[maxn]; void add(int u, int v) { to[cnp] = v, last[cnp] = head[u], head[u] = cnp ++; to[cnp] = u, last[cnp] = head[v], head[v] = cnp ++; } }E; int dfs(int u) { int ans = 1; mark[u] = 1; for(int i = E.head[u]; i; i = E.last[i]) { int v = E.to[i]; if(mark[v]) continue; ans += dfs(v); } return ans; } signed main() { int T = read(); while(T --) { n = read(), m = read(); E.cnp = 1; for(int i = 1; i <= n; i ++) E.head[i] = 0, mark[i] = 0; for(int i = 1; i <= m; i ++) { int x = read(), y = read(); E.add(x, y); E.add(y, x); } int num = n * (n - 1) / 2 - m; if(n & 1) { if(num & 1) printf("First\n"); else printf("Second\n"); continue; } int X = dfs(1), Y = dfs(n); if((X & 1) == (Y & 1)) { if(num & 1) { if(!(X & 1)) printf("First\n"); else printf("Second\n"); } else { if(X & 1) printf("First\n"); else printf("Second\n"); } } else printf("First\n"); } return 0; }
F.
考虑最后能否实现所有的边均为蓝色。易知对一个点进行重复操作并无意义。所以每个点只有操作与不操作的两种可能,一条边能够变成蓝色的条件即为两个端点中有且仅有一个点被操作。所以满足题意的图即为连通二分图。
考虑枚举了其中一个点集之后,能够连接的边数即为 m - e[S'] - e[T] (e[S] 即为点集 S 包含的边)。如果不考虑连通性那么方案数 \(g[S] = \sum 2^{m - e[S'] - e[T]}\)。首先通过枚举子集+预处理可以求出所有的g。之后求 f[S] 即为S点集的二分染色且保证连通的方案数,可以考虑容斥-总共的-不连通的方案数。考虑所有不连通的方案,我们枚举与1相连的点的子集,这部分的方案数为 f[S']。剩下的点随意连边(只是不与1的连通块相连),方案数为 g[T]。递推可以求出 f[全集],即为答案。
#include <bits/stdc++.h> using namespace std; #define mod 998244353 #define maxn 2000000 int n, m, bits[maxn], f[maxn], g[maxn], inv2, e[maxn]; struct edge { int x, y; }E[maxn]; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } int mul(int x, int y) { return 1ll * x * y % mod; } void Up(int &x, int y) { x += y; if(x >= mod) x -= mod; } int sub(int x, int y) { x -= y; if(x < 0) x += mod; return x; } int Qpow(int x, int t) { int base = 1; for(; t; t >>= 1, x = mul(x, x)) if(t & 1) base = mul(base, x); return base; } bool in(int x, int st) { return st & bits[x - 1]; } void pt(int a) { int b[1000]; int cnt = 0; for(int i = 1; i <= n; i ++) b[i] = 0; while(a) { b[++ cnt] = a & 1; a >>= 1; } for(int i = n; i; i --) cout << b[i]; cout << " "; } void Pre_dfs(int now, int st) { if(now == (n + 1)) { for(int i = 1; i <= m; i ++) if(in(E[i].x, st) && in(E[i].y, st)) e[st] ++; return; } Pre_dfs(now + 1, st); Pre_dfs(now + 1, st | bits[now - 1]); } void Get_G() { for(int S = 0; S < bits[n]; S ++) { for(int T = S; T >= 0; T = (T - 1) & S) { Up(g[S], bits[e[S] - e[T] - e[S ^ T]]); if(T == 0) break; } } } void Get_F() { for(int S = 0; S < bits[n]; S ++) { f[S] = mul(g[S], inv2); if(!(S & 1)) continue; for(int T = (S - 1) & S; T; T = (T - 1) & S) { if(!(T & 1)) continue; f[S] = sub(f[S], mul(f[T], g[S ^ T])); } } } int main() { n = read(), m = read(); bits[0] = 1; inv2 = Qpow(2, mod - 2); for(int i = 1; i <= max(n, m); i ++) bits[i] = mul(bits[i - 1], 2); for(int i = 1; i <= m; i ++) E[i].x = read(), E[i].y = read(); Pre_dfs(1, 0); Get_G(); Get_F(); printf("%d\n", f[bits[n] - 1]); return 0; }