题意
给你一个图,所有过1号点的简单环大小不超过3,可以任意删去与1号点相连的边,求有多少种删法使任意通过一号点的平凡环边权异或和不为0(边权小于32).平凡环:从某个点出发,经过任意多点最终回到起始点,过程中边和点都可以重复经过,但是必须有某条边只经过了奇数次。
思路
首先可以看出大概是以1号点为根分成若干枝,每个枝有1个或两个相邻点与1有边。每次从1出发走任意多个分支回到1,但是每个分支只能由一个简单环产生作用。到此可以确定思路为dp。考虑状态:首先可以dfs每个分支,只需考虑返祖边代表的环(任意一个简单环一定可以由若干返祖边代表的环的异或和得到)。于是可以得到每个分支所能贡献的异或和的种类(可以理解为返祖边所构成的向量空间),有两个与1相连的点的分支有2种状态,其他1种。考虑5维向量空间的子空间只有374种(一开始以为是 2 32 2^{32} 232,思考了一下大概 3 2 4 32^4 324,后来我跑了一下按照线性基状压,发现要大概3W个状态,看了题解发现如果按照向量空间种类计算只有374个不同状态),可以提前预处理转移,预处理用map的话是O( 1024 S 2 l o g S 1024S^2logS 1024S2logS)。总复杂度O( S N + 1024 S 2 l o g S SN+1024S^2logS SN+1024S2logS)
#include <bits/stdc++.h>
using namespace std;
struct Edge {
int to,next,w;
}bus[303030];
int tic;
int n,m,vis[101010],dis[101010],st[101010],z = 1,aa[101010],bb[101010],cc[101010],ppp[101010],dfn[101010];
int root[101010],p[101010],t[101010];
vector<int> ex[101010];
bool ban[101010];
long long status[1010],f[2][404] = {0};
int nxt[1010][32] = {0},mo[505][505];
int len = 0,tail = 0;
map<long long,int> M;
const int mod = 1000000007;
inline void dfs(int now,int last) {
int to;
for (int i = st[now];i;i = bus[i].next) {
to = bus[i].to;
if (to == 1 || i == (last^1))
continue;
if (vis[to] == tic) {
if (dfn[to] < dfn[now]) {
if ((dis[now]^dis[to]^bus[i].w) == 0)
ban[tic] = 1;
ex[tic].push_back(dis[now]^dis[to]^bus[i].w);
}
}
else {
vis[to] = tic;
dfn[bus[i].to] = dfn[now]+1;
dis[bus[i].to] = (dis[now]^bus[i].w);
dfs(bus[i].to,i);
}
}
}
inline void AddE(int from,int to,int w) {
bus[++z].to = to;
bus[z].w = w;
bus[z].next = st[from];
st[from] = z;
}
inline void init() {
memset(nxt,-1,sizeof(nxt));
for (int i = 1;i < 32; i++) {
status[++tail] = ((1LL<<i)|1);
M[status[tail]] = tail;
}
for (int i = 1;i <= 4; i++) {
len = tail;
for (int j = 1;j <= len; j++) {
for (int k = 1;k < 32; k++) {
if (!((1LL<<k)&status[j])) {
long long tmp = status[j];
for (int ii = 0;ii < 32; ii++) {
if ((1LL<<ii)&status[j]) {
tmp |= (1LL<<(ii^k));
}
}
if (!M[tmp]) {
M[tmp] = ++tail;
status[tail] = tmp;
}
nxt[j][k] = M[tmp];
}
}
}
}
}
inline int calc(int A,int B) {
long long tmp = 0;
for (int i = 0;i < 32; i++)
if ((1LL<<i)&status[A]) {
for (int j = 0;j < 32; j++)
if ((1LL<<j)&status[B]) {
if (tmp&(1LL<<(i^j)))
return -1;
tmp |= (1LL<<(i^j));
}
}
return M[tmp];
}
int main() {
init();
status[374] = 1;
tail = 374;
M[1] = 374;
scanf("%d%d",&n,&m);
for (int i = 1;i <= m; i++) {
scanf("%d%d%d",&aa[i],&bb[i],&cc[i]);
if (aa[i] == 1) {
root[bb[i]] = 1;
dis[bb[i]] = cc[i];
}
if (bb[i] == 1)
root[aa[i]] = 1,dis[aa[i]] = cc[i];
}
for (int i = 1;i <= m; i++) {
if (root[aa[i]] && root[bb[i]]) {
p[aa[i]] = bb[i];
p[bb[i]] = aa[i];
ppp[aa[i]] = ppp[bb[i]] = (cc[i]^dis[aa[i]]^dis[bb[i]]);
}
AddE(aa[i],bb[i],cc[i]);
AddE(bb[i],aa[i],cc[i]);
}
for (int i = 2;i <= n; i++)
if (root[i]) {
tic = i;
vis[i] = i;
dfn[i] = 0;
dfs(i,-10);
t[i] = 374;
if (ex[i].size()) {
for (int j = ex[i].size()-1;~j; j--) {
if (!ex[i][j]){
ban[i] = 1;
break;
}
if (!mo[ex[i][j]][t[i]])
mo[ex[i][j]][t[i]] = mo[t[i]][ex[i][j]] = calc(t[i],ex[i][j]);
if (mo[ex[i][j]][t[i]] < 0) {
ban[i] = 1;
break;
}
t[i] = mo[ex[i][j]][t[i]];
}
}
}
int I = 0;
f[I][374] = 1;
for (int i = 2;i <= n; i++) {
if (ban[i])
continue;
if (!root[i])
continue;
ban[i] = ban[p[i]] = 1;
int pp = t[p[i]],pppp = ppp[i];
if (!mo[t[i]][pppp])
mo[t[i]][pppp] = mo[pppp][t[i]] = calc(pppp,t[i]);
pppp = mo[pppp][t[i]];
if (ppp[i] == 0)
pppp = -1;
for (int j = 0;j <= 400; j++)
f[I^1][j] = 0;
for (int j = 1;j <= 374; j++) {
f[I][j] %= mod;
f[I^1][j] += f[I][j];
if (!mo[j][t[i]])
mo[j][t[i]] = mo[t[i]][j] = calc(j,t[i]);
if (mo[j][t[i]] > 0) {
f[I^1][mo[j][t[i]]] += f[I][j];
}
if (p[i]) {
if (!mo[j][pp])
mo[j][pp] = mo[pp][j] = calc(j,pp);
if (mo[j][pp] > 0)
f[I^1][mo[j][pp]] += f[I][j];
if (pppp > 0) {
if (!mo[j][pppp])
mo[j][pppp] = mo[pppp][j] = calc(j,pppp);
if (mo[j][pppp] > 0)
f[I^1][mo[j][pppp]] += f[I][j];
}
}
}
I ^= 1;
}
for (int j = 2;j <= 374; j++)
f[I][1] += f[I][j];
f[I][1] %= mod;
printf("%lld",f[I][1]);
return 0;
}
/*
6 8
1 2 0
2 3 1
2 4 3
2 6 2
3 4 8
3 5 4
5 4 5
5 6 6
*/