【题解】P5169 xtq的异或和
再强没有 xtq!!!1
思路
多项式的正确用法。
首先根据 P4151 [WC2011]最大XOR和路径 的神秘结论,这里只需要任意求出原图的一棵生成树,以及所有只包含一条非树边的简单环就可以维护原图的所有路径了。
对于两点 \(u, v\),令 \(\operatorname{dis}(u)\) 为生成树上点 \(u\) 到根的路径异或和,\(x\) 为原图中任意一个合法简单环的权值,则原图中 \(u, v\) 之间所有路径的贡献等价于 \(\operatorname{dis}(u) \oplus \operatorname{dis}(v) \oplus x\) 的贡献。
于是想到用线性基一类的东西去维护每一个合法环的权值,但是这里有更简单的做法。
考虑令 \(G[i]\) 表示是否存在异或和为 \(i\) 的合法环。每次插入一个权值为 \(x\) 的合法环时,\(\forall i\),如果 \(G[x] = 0\),则 \(G[i \oplus x]\) 也一定等于 \(0\),不然可以反推出 \(G[x] = 1\).
每次暴力插入完之后 \(\sum\limits_{i} G[i]\) 至少翻倍,所以时间复杂度是 \(O(V \log V)\).
令 \(F[i] = \sum\limits_{j = 1}^n [\operatorname{dis}(j) = i]\),那么询问 \(x\) 时答案为 \(\sum\limits_{i \oplus j = x} F[i] \cdot F[i] \cdot G[j]\).
发现实际上是一个位运算卷积的形式,考虑上 FWT 就可以 \(O(n \log n)\) 做了。
代码
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const int maxm = 3e5 + 5;
const int lim = 262144;
const int mod = 998244353;
struct node
{
int u, v, w;
bool vis;
} edge[maxm];
int n, m, q;
int dis[maxn];
ll F[lim], G[lim];
bool vis[maxn];
vector<int> g[maxn], w[maxn], idx[maxn];
void dfs(int u)
{
vis[u] = true;
F[dis[u]]++;
for (int i = 0; i < g[u].size(); i++)
{
int v = g[u][i], d = w[u][i], id = idx[u][i];
if (vis[v]) continue;
dis[v] = dis[u] ^ d;
edge[id].vis = true;
dfs(v);
}
}
void FWT(ll *F, int n)
{
for (int len = 2, m = 1; len <= n; m = len, len <<= 1)
{
for (int l = 0, r = len - 1; r <= n; l += len, r += len)
{
for (int p = l; p < l + m; p++)
{
ll val = F[p];
F[p] = F[p] + F[p + m];
F[p + m] = val - F[p + m];
}
}
}
}
void IFWT(ll *F, int n)
{
for (int len = 2, m = 1; len <= n; m = len, len <<= 1)
{
for (int l = 0, r = len - 1; r <= n; l += len, r += len)
{
for (int p = l; p < l + m; p++)
{
ll val = F[p];
F[p] = (F[p] + F[p + m]) / 2;
F[p + m] = (val - F[p + m]) / 2;
}
}
}
}
int main()
{
scanf("%d%d%d", &n, &m, &q);
for (int i = 1, u, v, d; i <= m; i++)
{
scanf("%d%d%d", &u, &v, &d);
edge[i] = (node){u, v, d};
g[u].push_back(v), w[u].push_back(d), idx[u].push_back(i);
g[v].push_back(u), w[v].push_back(d), idx[v].push_back(i);
}
dfs(1);
G[0] = 1;
for (int i = 1; i <= m; i++)
{
if (!edge[i].vis)
{
int res = edge[i].w ^ dis[edge[i].u] ^ dis[edge[i].v];
if (!G[res])
for (int j = 0; j < lim; j++)
if (G[j]) G[j ^ res] = 1;
}
}
FWT(F, lim), FWT(G, lim);
for (int i = 0; i < lim; i++) F[i] = F[i] * F[i] * G[i];
IFWT(F, lim);
while (q--)
{
int x;
scanf("%d", &x);
printf("%lld\n", F[x] % mod);
}
return 0;
}