AtCoder AGC043C Giant Graph

洛谷传送门

AtCoder 传送门

学长讲的一道神仙题。

思路

由于 \(10^{18}\) 非常大,所以可以考虑这样一个贪心:每次取目前能取的 \(x+y+z\) 最大的点。因此先将所有边定向,从小的编号连向大的。

\(f_{x,y,z}\) 为是否选 \((x,y,z)\),那么 \(f_{x,y,z} = \prod\limits_{(x,y,z) \to (x',y',z')} [f_{x',y',z'} = 0]\),其中 \((x,y,z) \to (x',y',z')\) 表示图上有一条 \((x,y,z)\)\((x',y',z')\) 的边。意思就是,若 \((x,y,z)\) 的所有出边都没选,则 \((x,y,z)\) 可以选,否则不能选。

暴力算是 \(O(n^3)\) 的,卡卡常就能过去了。

考虑图上的博弈问题:一个结点上有标记,有两个人轮流移动,每次移动可以将一个标记移动到它的任意一条出边所连向的另一个结点,无法移动的人输。

那么我们发现三个图都是独立的,并且必败态相当于 dp 中的 \(f_{x,y,z} = 1\)。因此将三个图的 \(\mathrm{SG}\) 函数算出来再异或得到整个博弈的 \(\mathrm{SG}\) 函数,答案为 \(\sum\limits_{sg_0(x)\, \oplus\, sg_1(y)\, \oplus\, sg_2(z)\, =\, 0} 10^{18(x+y+z)}\)

注意到对于 \(m\) 条边的图的 \(\mathrm{SG}\) 函数,其最大值是 \(O(\sqrt{m})\) 级别的,因此暴力枚举 \(sg_0(x)\)\(sg_1(y)\) 再根据异或的性质得到 \(sg_2(z)\)。预处理出每张图对应 \(\mathrm{SG}\) 函数的和,再相乘得到答案。总时间复杂度为 \(O(n + m)\)

代码

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;
}
posted @ 2022-07-11 22:45  zltzlt  阅读(62)  评论(0编辑  收藏  举报