CF1299D Around the World

就是是否能有为 $0$ 的路径直接会想到线性基,也就是里面的环是可以走或者不走的。

我们写一手暴力发现大小为 $5$ 的线性基的个数不会很多,那么我们可以考虑对于每个联通块存一个线性基。

如果说联通块内部已经有环线性相关那么肯定是不能连边的。

如果线性无关那么考虑题目给出的性质,也就是和 $1$ 相连的环大小最多为 $3$。那么我们对于一个联通块最多有 $2$ 个点和 $1$ 相连。我们进行分类讨论有 $2$ 条边相连的情况。

  • 断掉两条边也就是直接加入即可。
  • 断掉一条边也是同理,枚举一下断掉那条边。
  • 不断边,也就是意味着多一个 $3$ 元环我们需要将其加入并且判断线性关系。

不妨设 $dp(i, j)$ 表示考虑到底 $i$ 个联通块,之前的联通块的连接关系构成的线性基为 $j$。

设当前联通块的线性基是 $B$。

我们所有的情况都可以不加入,所以 $dp(i - 1, j) \to dp(i, j)$。

只有一条边 $dp(i - 1, j) \to dp(i, j \cup B)$。

如果有两条边
$$
dp(i - 1, j) \times 2 \to dp(i, j \cup B) \
dp(i - 1, j) \to dp(i, j \cup B \cup w)
$$
前面是判断断那条边,另外是考虑多加入一个 $3$ 元环的贡献。


我们只需要对于每个联通块维护和 $1$ 相连的三元环异或值,自己内部的线性基即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#include <bits/stdc++.h>
using namespace std;

//#define Fread
#define Getmod

#ifdef Fread
char buf[1 << 21], *iS, *iT;
#define gc() (iS == iT ? (iT = (iS = buf) + fread (buf, 1, 1 << 21, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
#define getchar gc
#endif // Fread

template <typename T>
void r1(T &x) {
x = 0;
char c(getchar());
int f(1);
for(; c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(; '0' <= c && c <= '9';c = getchar()) x = (x * 10) + (c ^ 48);
x *= f;
}

#ifdef Getmod
const int mod = 1e9 + 7;
template <int mod>
struct typemod {
int z;
typemod(int a = 0) : z(a) {}
inline int inc(int a,int b) const {return a += b - mod, a + ((a >> 31) & mod);}
inline int dec(int a,int b) const {return a -= b, a + ((a >> 31) & mod);}
inline int mul(int a,int b) const {return 1ll * a * b % mod;}
typemod<mod> operator + (const typemod<mod> &x) const {return typemod(inc(z, x.z));}
typemod<mod> operator - (const typemod<mod> &x) const {return typemod(dec(z, x.z));}
typemod<mod> operator * (const typemod<mod> &x) const {return typemod(mul(z, x.z));}
typemod<mod>& operator += (const typemod<mod> &x) {*this = *this + x; return *this;}
typemod<mod>& operator -= (const typemod<mod> &x) {*this = *this - x; return *this;}
typemod<mod>& operator *= (const typemod<mod> &x) {*this = *this * x; return *this;}
int operator == (const typemod<mod> &x) const {return x.z == z;}
int operator != (const typemod<mod> &x) const {return x.z != z;}
};
typedef typemod<mod> Tm;
#endif

template <typename T,typename... Args> inline void r1(T& t, Args&... args) {
r1(t); r1(args...);
}

//#define int long long
const int maxn = 2e5 + 5;
const int maxm = 374 + 5;
const int maxK = 31744 + 5;

int head[maxn], cnt;
struct Edge {
int to, next, w;
}edg[maxn << 1];

void add(int u,int v,int w) {
edg[++ cnt] = (Edge) { v, head[u], w }, head[u] = cnt;
}

const int N = 4;
struct Seg {
int a[5]; Seg(void) { memset(a, 0, sizeof(a)); }
bool Insert(int x) {
for(int i = N; ~ i; -- i) if((x >> i) & 1) {
if(a[i]) x ^= a[i];
else {
a[i] = x;
for(int j = i - 1; ~ j; -- j) if((a[i] >> j) & 1) a[i] ^= a[j];
for(int j = N; j > i; -- j) if((a[j] >> i) & 1) a[j] ^= a[i];
return true;
}
}
return false;
}
int Hash() {
return (a[0] | (a[1] << 1) | (a[2] << 3) | (a[3] << 6) | (a[4] << 10));
}
}b[maxm], c[maxn];
int tr[maxm][maxm];
int id[maxK], num(0);

//int Mx(0);

void dfs(Seg p) {
int hs = p.Hash(); if(id[hs]) return ;
id[hs] = ++ num, b[num] = p;
// Mx = max(Mx, hs);
for(int i = 1; i <= 31; ++ i) {
Seg np = p;
if(np.Insert(i)) dfs(np);
}
}

void init() {
dfs(Seg());
// printf("Mx = %d\n", Mx);
// puts("ZZZ");
// printf("num = %d\n", num);
for(int i = 1; i <= num; ++ i) {
for(int j = 1; j <= num; ++ j) {
Seg tmp = b[i]; bool flag(1);
for(int k = 0; k <= 4; ++ k) if(b[j].a[k]) flag &= tmp.Insert(b[j].a[k]);
if(flag) tr[i][j] = id[tmp.Hash()];
}
}
}

int n, m;
bool pd[maxn];
int dis[maxn], Siz(0), st[maxn], Ec[maxn];
int bel[maxn], dfn[maxn], dfntot(0);
bool sed[maxn];
void dfs1(int p,int pre) {
dfn[p] = ++ dfntot, bel[p] = Siz;
for(int i = head[p];i;i = edg[i].next) {
int to = edg[i].to;
if(to == 1 || to == pre) continue;
if(!bel[to]) dis[to] = edg[i].w ^ dis[p], dfs1(to, p);
else if(dfn[p] > dfn[to]) pd[Siz] &= c[Siz].Insert(dis[p] ^ dis[to] ^ edg[i].w);
}
}

signed main() {
// freopen("S.in", "r", stdin);
// freopen("S.out", "w", stdout);
int i, j;
r1(n, m); init();
for(i = 1; i <= m; ++ i) {
int u, v, w;
r1(u, v, w), add(u, v, w), add(v, u, w);
}
for(i = head[1];i;i = edg[i].next) {
int to = edg[i].to, w = edg[i].w;
if(!bel[to]) {
pd[++ Siz] = 1, Ec[Siz] = w, st[Siz] = to;
dfs1(to, 0);
}
else {
for(int j = head[to]; j; j = edg[j].next) {
int to = edg[j].to, w1 = edg[j].w;
if(st[bel[to]] == to) {
sed[bel[to]] = 1, Ec[bel[to]] ^= w ^ w1;
}
}
}
}

vector<vector<Tm> > dp(Siz + 2, vector<Tm>(num + 2, 0) );
dp[0][id[0]] = 1;
for(i = 1; i <= Siz; ++ i) {
for(j = 1; j <= num; ++ j) dp[i][j] = dp[i - 1][j];
if(!pd[i]) continue;
if(!sed[i]) {
int hs = id[c[i].Hash()];
for(j = 1; j <= num; ++ j) if(tr[j][hs])
dp[i][tr[j][hs]] += dp[i - 1][j];
}
else {
int hs = id[c[i].Hash()];
bool flag = c[i].Insert(Ec[i]);
int hs1 = id[c[i].Hash()];
for(j = 1; j <= num; ++ j) {
if(tr[j][hs]) dp[i][tr[j][hs]] += dp[i - 1][j] * 2;
if(flag && tr[j][hs1]) dp[i][tr[j][hs1]] += dp[i - 1][j];
}
}
}
Tm ans(0);
for(i = 1; i <= num; ++ i) ans += dp[Siz][i];
printf("%d\n", ans.z);
return 0;
}