构造一个
- 给定
张 个点的无向图 。 - 构造
个点,标号是一个三元组 。 - 对于
中的边 ,连边 和 。 - 对于
中的边 ,连边 和 。 - 对于
中的边 ,连边 和 。 - 对于
上的一个点 ,其点权为 。
现在,要你求出
考虑到是点权是一个大数的幂次,那肯定是选的数越大越好。
考虑如果你知道了图
首先
然后如果一个点已经确定了旁边已经比她大的点都不选了,那么她才会选。
那会不会有两个相邻的点点权相等呢?显然不会。因为相邻的点必须有两个权值相等,那么另外一个肯定相等,这是不合法的。
然后我们就考虑这个过程,就是我们考虑如何判断一个点选不选,那就要看与之相邻的比她大的点选不选,就是如果那些点都是不合法的,那么这个点合法,否则不合法。
我们发现这跟博弈问题很像,就是有
于是我们只要计算每个点的 sg 值即可。
考虑到每张图 sg 值只会有
cpp
namespace dfcmd {
typedef long long LL;
const int maxn = 100005;
const int mod = 998244353;
const int di = 1000000000000000000LL % mod;
int bin[maxn];
inline void add(int& x, int y) {
x += y;
if (x >= mod) {
x -= mod;
}
}
int n;
struct myhnb {
int m;
int sg[maxn];
int ss[maxn];
struct Edge {
int to, nxt;
} e[maxn];
int first[maxn];
int cnt = 0;
inline void add_edge(int u, int v) {
if (u > v) {
swap(u, v);
}
e[++cnt].nxt = first[u];
first[u] = cnt;
e[cnt].to = v;
}
int vis[maxn];
myhnb() {
memset(first, 0, sizeof(first));
memset(ss, 0, sizeof(ss));
memset(vis, 0, sizeof(vis));
}
inline void Dfs() {
for (int now = n; now; --now) {
vis[now] = true;
set<int> sgg;
sgg.clear();
for (int i = first[now]; i; i = e[i].nxt) {
sgg.insert(sg[e[i].to]);
}
sg[now] = 0;
while (sgg.count(sg[now])) {
sg[now]++;
}
add(ss[sg[now]], bin[now]);
}
}
inline int& operator [] (int x) {
return ss[x];
}
} d[3];
int main() {
read(n);
bin[0] = 1;
for (int i = 1; i <= n; ++i) {
bin[i] = (LL) bin[i - 1] * di % mod;
}
for (int i = 0; i < 3; ++i) {
read(d[i].m);
for (int j = 1; j <= d[i].m; ++j) {
int u, v;
read(u), read(v);
d[i].add_edge(u, v);
}
d[i].Dfs();
}
int ans = 0;
for (int i = 0; i <= 1000; ++i) {
for (int j = 0; j <= 1000; ++j) {
add(ans, (LL) d[0][i] * d[1][j] % mod * d[2][i ^ j] % mod);
}
}
writeln(ans);
return 0;
}
}