根号分治 + dsu on tree + 换根:CSP26-9 T5

image-20260926172746883

TUOJ


因为 m≤40m\le 40,我们考虑把颜色信息压缩成二进制。那一条路径合法当且仅当其路径异或和下有1个1。

设从根到每个点深度为 dud_u,异或和为 sus_u。考虑 u,vu,v 和其LCA xx,显然题目中两个限制条件转化为:

  • du+dv−2dx≡0(modk)d_u+d_v-2d_x\equiv 0\pmod k
  • su⊕sv⊕cx=2js_u\oplus s_v\oplus c_x=2^j

我们考虑根号重构来维护。

考虑每次计算答案,我们采用dsu on tree。显然上面这几个东西是很容易用哈希之类的东西维护的。但为了方便我们后面暴力的计算,我们要进行两次dsu on tree来换根。因为我们要求一个 sumxsum_x 代表和 xx 匹配的亮点有多少个。

考虑块内暴力求的过程。上面的过程1可以用 O(1)O(1) LCA来搞,过程2可以用 __builtin_popcountll() 来弄。

复杂度 O(qB⋅mnlog⁡n+qB)O(\dfrac q B\cdot mn\log n+qB)

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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#include<bits/stdc++.h>
#include<cassert>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stdout, ##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
#define ll long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define pb push_back
#define N 100010
#define fi first
#define se second
//#define M
//#define mo
int n, m, i, j, k, T;
ll s[N], c[N], ans;
int w[N], son[N], d[N], p[N], q;
int u, v, wlen, K, sum[N], ff;
int dep[N], top, b[N], z[N];
vector<pair<int, int> >G[N];
vector<int>V[N][2];
unordered_map<ll, int>mp[N][4];
char str[N];

//int lca(int x, int y) {
// if(dep[y] > dep[x]) swap(x, y);
// for(int k = 20; k >= 0; --k)
// if(dep[f[x][k]] >= dep[y])
// x = f[x][k];
// if(x == y) return x;
// for(int k = 20; k >= 0; --k)
// if(f[x][k] != f[y][k])
// x = f[x][k], y = f[y][k];
// return f[x][0];
//}

struct LCA {
int f[N << 1][22], id[N], top, Log2[N << 1];
void add(int x) {
id[x] = ++top;
f[top][0] = x;
}
void work() {
Log2[0] = -1;
for(k = 1; k <= 20; ++k)
for(i = 1; i <= top; ++i) {
Log2[i] = Log2[i >> 1] + 1;
if(i + (1 << k) - 1 > top) continue;
int u = f[i][k - 1], v = f[i + (1 << k - 1)][k - 1];
f[i][k] = (dep[u] < dep[v] ? u : v);
}
}
int qry(int x, int y) {
int l = id[x], r = id[y], k;
if(l > r) swap(l, r);
k = Log2[r - l + 1];
int u = f[l][k], v = f[r - (1 << k) + 1][k];
return (dep[u] < dep[v] ? u : v);
}
}lca;

void dfs(int x, int fa) {
dep[x] = dep[fa] + 1; lca.add(x);
s[x] ^= c[x]; w[x] = 1;
for(auto t : G[x]) if(t.fi != fa) {
int y = t.fi;
s[y] = s[x]; d[y] = (d[x] + t.se) % K;
dfs(y, x); w[x] += w[y]; lca.add(x);
if(w[y] > w[son[x]]) son[x] = y;
}
}

int C(int x) {
return (x % K + K) % K;
}

void dfs2(int x, int fa) {
int y = son[x], z, &k = p[x], i, dep;
ll bin;
k = x;
if(y) dfs2(y, x), k = p[y];
for(i = 0; i < m; ++i) {
bin = (s[x] ^ c[x] ^ (1ll << i));
auto it = mp[k][d[x]].find(bin);
if(it != mp[k][d[x]].end())
sum[x] += it->second;
}
V[k][str[x]].pb(x);
if(str[x]) mp[k][d[x]][s[x]]++;
for(auto t : G[x]) if(t.fi != fa && t.fi != y) {
z = t.fi; dfs2(z, x);
auto Value = [&] (int o) -> void {
for(auto t : V[p[z]][o]) {
dep = C(2 * d[x] - d[t]);
for(i = 0; i < m; ++i) {
bin = (s[t] ^ c[x] ^ (1ll << i));
auto it = mp[k][dep].find(bin);
if(it != mp[k][dep].end())
sum[t] += it->second;
}
}
};
Value(0); Value(1);
for(auto t : V[p[z]][0]) V[k][0].pb(t);
for(auto t : V[p[z]][1]) {
V[k][1].pb(t); mp[k][d[t]][s[t]]++;
}
V[p[z]][0].clear(); V[p[z]][1].clear();
for(i = 0; i < K; ++i) mp[p[z]][i].clear();
}
// debug("For %lld[%lld]:\n", x, k);
// for(auto t : V[k][0]) debug("%lld ", t); debug("\n");
// for(auto t : V[k][1]) debug("%lld ", t); debug("\n");
}

void dfs3(int x, int fa) {
// debug("%lld -> %lld [%lld]\n", fa, x, son[x]);
int y = son[x], z, k = p[x], i, dep;
ll bin;
reverse(G[x].begin(), G[x].end());
for(auto t : G[x]) if(t.fi != fa && t.fi != y) {
z = t.fi; p[z] = z; dfs3(z, x);
auto Value = [&] (int o) -> void {
for(auto t : V[p[z]][o]) {
dep = d[t];
for(i = 0; i < m; ++i) {
bin = (s[t] ^ (1ll << i));
auto it = mp[k][dep].find(bin);
if(it != mp[k][dep].end())
sum[t] += it->second;
}
}
};
Value(0); Value(1);
for(auto t : V[p[z]][0]) V[k][0].pb(t);
for(auto t : V[p[z]][1]) {
V[k][1].pb(t);
dep = C(2 * d[x] - d[t]);
mp[k][dep][s[t] ^ c[x]]++;
}
V[p[z]][0].clear(); V[p[z]][1].clear();
for(i = 0; i < K; ++i) mp[p[z]][i].clear();
}
reverse(G[x].begin(), G[x].end());
for(i = 0; i < m; ++i) {
bin = (s[x] ^ (1ll << i));
auto it = mp[k][d[x]].find(bin);
if(it != mp[k][d[x]].end())
sum[x] += it->second;
}
if(str[x]) mp[k][d[x]][s[x] ^ c[x]]++;
V[k][str[x]].pb(x);
if(y) p[y] = k, dfs3(y, x);
}

void Make() {
for(i = 1; i <= n; ++i) sum[i] = 0, p[i] = i; ans = 0;
dfs2(1, 0);
V[p[1]][0].clear(); V[p[1]][1].clear();
for(i = 0; i < K; ++i) mp[p[1]][i].clear();
// for(i = 1; i <= n; ++i) debug("%lld ", sum[i]); debug("\n");
for(i = 1; i <= n; ++i) p[i] = i;
dfs3(1, 0);
V[p[1]][0].clear(); V[p[1]][1].clear();
for(i = 0; i < K; ++i) mp[p[1]][i].clear();
// for(i = 1; i <= n; ++i) debug("%lld ", str[i]); debug("\n");
// for(i = 1; i <= n; ++i) debug("%lld ", sum[i]); debug("\n");
for(i = 1; i <= n; ++i)
if(str[i]) ans += sum[i] + 2;
ans /= 2;
}

signed main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
n = read(); m = read(); K = read(); q = read();
int B = 4000;
scanf("%s", str + 1);
for(i = 1; i <= n; ++i) str[i] -= '0';
for(i = 1; i <= n; ++i) c[i] = read() - 1, c[i] = (1ll << c[i]);
for(i = 1; i < n; ++i) {
u = read(); v = read(); wlen = read() % K;
// debug("%lld --- %lld | %lld\n", u, v, wlen);
G[u].pb({v, wlen}); G[v].pb({u, wlen});
}
dfs(1, 0); lca.work();
Make(); printf("%lld\n", ans);
for(int qx = 1; qx <= q; ++qx) {
u = read(); str[u] ^= 1;
if(qx % B == 0) {
Make(); top = 0;
} else {
int o = (str[u] == 0 ? -1 : 1); ans += o;
if(str[u]) ans += sum[u]; else ans -= sum[u];
for(i = 1; i <= top; ++i) {
v = z[i]; ff = lca.qry(u, v);
if(v == u) continue;
if(C(d[u] + d[v] - 2 * d[ff])) continue;
ll k = (s[u] ^ s[v] ^ c[ff]);
// debug("%lld %lld %lld [%lld %lld]\n", u, v, ff, k, __builtin_popcount(k));
if(__builtin_popcountll(k) == 1ll) {
ans += o * b[i];
}
}
z[++top] = u; b[top] = o;
}
printf("%lld\n", ans);
}
return 0;
}