auto n = nextInt(), m = nextInt(); FWS::init(n); auto cnt = m; while (m --) { int u = nextInt(), v = nextInt(); FWS::addEdge(u, v, 0); FWS::addEdge(v, u, 0); } dfn[0] = 0, dfs(1, 0); for (int i = 1; i <= n; ++ i) if (!vis[i]) dfs(i, 0); for (auto ii : ring) cnt -= ii; auto ans = fastPow(2, cnt); for (auto ii : ring) { auto t = fastPow(2, ii); t = (t - 1 + mod) % mod; ans = ans * t % mod; } printf("%lld\n", ans);
那么我们可以发现,原本 KMP 中的 border 的前缀部分现在变成后缀,后缀变成了前缀;现在的后缀和前缀依然是相等的;这样就符合了循环小数的定义:我们可以把字符串后缀除了 border 后缀部分的部分看作是循环节,后缀 border 看作是循环节的延申,就可以对于每一个后缀求出 p 和 l 了;
这里本来有张图,但是画错了……
需要注意的是这并没有覆盖全部的情况:比如字符串 ...ABCABCA,它只考虑了 l = 3, p = 4 和 l = 3, p = 7 的情况,没有考虑到 l = 6 和 p = 7 的情况,但是因为 a 和 b 都是正整数,所以显然 l 较小的时候比较占优势;因为 border 是最大的真公共子串,所以它一定会包含所有可以包含的部分,所以它总是会包含最优的 l;
string s; longs a, b; cin >> a >> b; getline(cin, s, '.'); getline(cin, s, '\n'); reverse(s.begin(), s.end()); vector<int> kmp; buildKMP(s.c_str(), kmp, s.size()); constauto siz = s.size(); longs ans = -0x3f3f3f3f3f3f3f3f; for (int p = 1; p <= siz; ++ p) { int l = p - kmp[p]; ans = max(ans, a * p - b * l); } cout << ans << endl;
usingnamespacestd; using longs = longlong; using uint = unsigned;
inlineintnextInt() { int x = 0, f = 0, ch = getchar(); while (!isdigit(ch)) ch == '-' && (f = !f), ch = getchar(); while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar(); return f ? -x : x; }
constint X = 110; char g[X][X]; int p[X], e[X]; int n, m, t, a, b; int h[X][X], v[X][X];
using number = int; // 设置流量数据类型 constint N = X * X * 3, M = N * 10;
structedge { int u, v, next; number w; edge() = default; edge(int u, int v, number w, int next) : u(u), v(v), w(w), next(next) {} };
namespace FWS { int head[N]; int tot; edge ee[M * 2];
using method = function<void(const edge &e)>;
voidinit(int n = N - 1) { memset(head, -1, sizeof(int) * (n + 1)); tot = 0; }
intaddEdge(int u, int v, number w) { ee[tot] = {u, v, w, head[u]}; return head[u] = tot ++; // 返回加入的边的编号,方便处理重边 }
voidforEach(int u, const method &iter) { for (auto ii = head[u]; ii != -1; ii = ee[ii].next) iter(ee[ii]); } }
namespace FN { constexprint inf = 0x3f3f3f3f; // 严格匹配 number,不要自动转型! int S, T, total = 0; // 重新建图之后的节点数
intaddEdge(int u, int v, number w) { auto pos = FWS::addEdge(u, v, w); FWS::addEdge(v, u, 0); return pos; } #ifdef USE_DINIC /** * Dinic 算法 * O (n²m) * * - 在残量网络上使用 BFS 构造分层图 * - 在分层图上 DFS 寻找增广路,并更新边权 * - 当前弧优化:避免寻找不可能增广的边 */ namespace Dinic { number dis[N]; int cur[N]; // 当前弧优化
// 先创建分层图 boolbfs() { usingnamespace FWS; staticqueue<int> q; memset(dis, 0x3f, sizeof(number) * (total + 1)); q.push(S); dis[S] = 0; while (!q.empty()) { int u = q.front(); q.pop(); for (int cc = head[u]; cc != -1; cc = ee[cc].next) { edge& e = ee[cc]; int v = e.v; auto w = e.w; cur[u] = head[u]; if (!w || dis[v] <= dis[u] + 1) continue; dis[v] = dis[u] + 1; q.push(v); } } return dis[T] != inf; }
number dfs(int u, number inflow) { usingnamespace FWS; if (u == T) return inflow; number outflow = 0, rest = inflow; for (int &cc = cur[u]; cc != -1; cc = ee[cc].next) // 当前弧优化 { edge &e = ee[cc], &r = ee[uint(cc) ^ 1u]; int v = e.v; auto w = e.w; if (!w || dis[v] != dis[u] + 1) continue; int t = dfs(v, min(w, rest)); outflow += t; e.w -= t; r.w += t; rest -= t; if (!rest) break; } if (!outflow) dis[u] = 0; return outflow; }
number go() { number maxFlow = 0; while (bfs()) maxFlow += dfs(S, inf); return maxFlow; } } #elif defined USE_ISAP /** * Improved Shortest Augumenting Path * O (n²m),比起 Dinic 算法只需要一次 BFS * * - 反向 BFS 并标记节点深度 * - 正向 DFS,用尽节点的出流时回溯加深深度 * - gap 优化:当某一深度不再包含节点时停止搜索 * - 当 S 的深度达到 n 时,搜索一定结束 * * 和 Dinic 类似,本算法也可以进行当前弧优化 */ namespace ISAP { int dep[N], gap[N]; // 节点的深度 & 特定深度节点的数量 number maxFlow = 0; int cur[N]; // 当前弧优化
voidbfs() { memset(dep, -1, sizeof dep); memset(gap, 0, sizeof gap); dep[T] = 0, gap[0] = 1; queue<int> q; q.push(T); usingnamespace FWS; while (!q.empty()) { auto u = q.front(); q.pop(); for (int cc = head[u]; cc != -1; cc = ee[cc].next) { edge& e = ee[cc]; int v = e.v; if (dep[v] != -1) continue; q.push(v); ++ gap[dep[v] = dep[u] + 1]; } } }
number dfs(int u, number flow) { if (u == T) return maxFlow += flow, flow; number used = 0; usingnamespace FWS; for (int cc = head[u]; cc != -1; cc = ee[cc].next) { edge& e = ee[cc], &r = ee[uint(cc) ^ 1u]; int v = e.v; auto w = e.w; if (!w || dep[v] + 1 != dep[u]) continue; auto t = dfs(v, min(w, flow - used)); if (t) e.w -= t, r.w += t, used += t; if (used == flow) return used; } if (-- gap[dep[u]] == 0) dep[S] = total + 1; ++ gap[++ dep[u]]; return used; }
voidsetInfo(int s, int t, int cnt) {FN::S = s, FN::T = t, FN::total = cnt;} }
boolin(int r, int c) {return r > 0 && r <= n && c > 0 && c <= m;}
// TODO: 思考这样处理的合理性? // 直接加两条边或者加重边没有区别 voidaddEdgeX(int u, int v) { FWS::addEdge(u, v, 1); FWS::addEdge(v, u, 1); }
voidbuildGraph() { usingnamespace FN; S = T = total = 0; FWS::init(2 + n * m * 2); for (int i = 1; i <= n; ++ i) memset(h[i], 0, sizeof(int) * (m + 1)), memset(v[i], 0, sizeof(int) * (m + 1)); S = ++ total, T = ++ total; for (int r = 1; r <= n; ++ r) for (int c = 1; c <= m; ++ c) { h[r][c] = ++ total, v[r][c] = ++ total; if (g[r][c] == '0') { if (in(r - 1, c) && g[r - 1][c] == '0') addEdgeX(v[r - 1][c], v[r][c]); if (in(r, c - 1) && g[r][c - 1] == '0') addEdgeX(h[r][c - 1], h[r][c]); addEdgeX(h[r][c], v[r][c]); } } for (int i = 1; i <= a; ++ i) addEdge(S, v[1][p[i]], 1); for (int i = 1; i <= b; ++ i) addEdge(v[n][e[i]], T, 1); }
cin >> t; while (t --) { cin >> n >> m >> a >> b; for (int i = 1; i <= n; ++ i) cin >> (g[i] + 1); for (int i = 1; i <= a; ++ i) cin >> p[i]; for (int i = 1; i <= b; ++ i) cin >> e[i]; buildGraph(); auto res = FN::Dinic::go(); cout << (res == a ? "Yes" : "No") << endl; }
但是链最终可以转化成上面说的那种必胜态的树型,只是先手后手必须一片叶子一片叶子的拿才可以;那么我们假设某长度为 x 的链的末端的叶子节点有一个度数大于等于 2 的祖先,设这个祖先的子节点是 a,那么问题就变成了怎么样删除节点让 a 节点成为叶子节点时玩家先手。形式化的说:假设当前树有 k 条链,链上当前的叶子节点和上述 a 节点的距离为 xi;每次可以选择部分 xi 使得它们减一,将其中一个减小到 0 的人输掉游戏。
usingnamespacestd; using longs = longlong; using uint = unsigned;
inlineintnextInt() { int x = 0, f = 0, ch = getchar(); while (!isdigit(ch)) ch == '-' && (f = !f), ch = getchar(); while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar(); return f ? -x : x; }
constint N = 1e6 + 5; int deg[N], p[N];
intmain() { ios::sync_with_stdio(false);
int t = nextInt(); while (t --) { int n = nextInt(); memset(deg, 0, sizeof(int) * (n + 1)); for (int i = 2; i <= n; ++ i) ++ deg[p[i] = nextInt()]; bool ok = true; for (int i = 1; i <= n; ++ i) if (!deg[i]) { auto x = i, len = 0; while (x && deg[p[x]] == 1) x = p[x], ++ len; ok &= bool(len % 2); } printf(!ok ? "Takeru\n" : "Meiya\n"); } return0; }