AtCoder AGC043C Giant Graph
学长讲的一道神仙题。
思路
由于 非常大,所以可以考虑这样一个贪心:每次取目前能取的 最大的点。因此先将所有边定向,从小的编号连向大的。
设 为是否选 ,那么 ,其中 表示图上有一条 到 的边。意思就是,若 的所有出边都没选,则 可以选,否则不能选。
暴力算是 的,卡卡常就能过去了。
考虑图上的博弈问题:一个结点上有标记,有两个人轮流移动,每次移动可以将一个标记移动到它的任意一条出边所连向的另一个结点,无法移动的人输。
那么我们发现三个图都是独立的,并且必败态相当于 dp 中的 。因此将三个图的 函数算出来再异或得到整个博弈的 函数,答案为 。
注意到对于 条边的图的 函数,其最大值是 级别的,因此暴力枚举 和 再根据异或的性质得到 。预处理出每张图对应 函数的和,再相乘得到答案。总时间复杂度为 。
代码
code
/* p_b_p_b txdy AThousandMoon txdy AThousandSuns txdy hxy txdy */ #include <bits/stdc++.h> #define pb push_back #define fst first #define scd second using namespace std; typedef long long ll; typedef pair<ll, ll> pii; const int maxn = 100100; const int maxm = 600; const ll mod = 998244353; const ll base = 1000000000000000000LL % mod; int n; struct graph { int m, mxk, sg[maxn]; bool vis[maxn]; vector<int> G[maxn]; ll f[maxm]; void dfs(int u) { if (vis[u]) { return; } vis[u] = 1; set<int> st; for (int v : G[u]) { dfs(v); st.insert(sg[v]); } sg[u] = 0; while (st.find(sg[u]) != st.end()) { ++sg[u]; } } void init() { scanf("%d", &m); while (m--) { int u, v; scanf("%d%d", &u, &v); if (u > v) { swap(u, v); } G[u].pb(v); } for (int i = 1; i <= n; ++i) { if (!vis[i]) { dfs(i); } } ll x = 1; for (int i = 1; i <= n; ++i) { mxk = max(mxk, sg[i]); x = x * base % mod; f[sg[i]] = (f[sg[i]] + x) % mod; } } } g[3]; void solve() { scanf("%d", &n); for (int i = 0; i < 3; ++i) { g[i].init(); } ll ans = 0; for (int i = 0; i <= g[0].mxk; ++i) { for (int j = 0; j <= g[1].mxk; ++j) { ans = (ans + g[0].f[i] * g[1].f[j] % mod * g[2].f[i ^ j] % mod) % mod; } } printf("%lld\n", ans); } int main() { int T = 1; // scanf("%d", &T); while (T--) { solve(); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通