这题思路很明显的,很快想到,然后写挂。好像写出来bug数不胜数。因此本文主要聚焦在debug过程,故称“改题报告”
Description
一棵树上,点有点权,边有边权。一个无序点对
(x,y)
的贡献是
(axxoray)×dis(x,y)
,即点权的异或乘上距离。
每次会修改一个点权,并查询修改后所有点对贡献之和。
Exam
写完一题后开始做,想了十几分钟就出了正解,然后看三题是网络流二合一,因为对网络流还是没这么熟悉,决定先做二题。又考虑了一下细节,然后就开始写。
写到11点过写完了(貌似这个写的速度还是不够啊),运行样例,一切行为也完全符合预期,感觉不错。若再要写三题,大概时间不够了(那时我还没想清楚如何建图)。于是上了个厕所,回来慢悠悠地写对拍。
结果拍了第一个就WA惨了。这时感到非常不妙,赶快用小数据调试。到考试结束时调了一个bug,可是还是WA。
悲惨的故事是,这时我慌忙把暴力交上去,结果没改文件名,输出文件是tree.ans
…
Debug
这是出错的关键部分:
long long sum[30050][30], sum_[30050][30], Ans;
int cnt[30050][30], cnt_[30050][30];
int f[30050][13], d[30050][13], t[30050];
int root, near;
void setroot(int x, int fa, int dis) {
f[x][t[x]] = root;
d[x][t[x]++] = dis;
for (int i = 0; i < 15; i++) {
register int j = i<<1 | a[x]>>i & 1;
sum_[near][j] += dis;
cnt_[near][j]++;
}
for (int i = h[x], u; i; i = nx[i])
if (!vis[u = to[i]] && u ^ fa)
setroot(u, x, dis+w[i]);
}
void divide(int rt) {
int S = dfssize(rt,0) >> 1;
while (max[rt] > S) rt = son[rt];
vis[rt] = true;
f[rt][t[rt]] = rt;
d[rt][t[rt]] = 0;
for (int i = 0; i < 15; i++)
cnt[rt][i<<1 | a[rt]>>i&1] = 1;
for (int i = h[rt]; i; i = nx[i])
if (!vis[to[i]]) {
setroot(near = to[i], root = rt, w[i]);
for (int i = 0; i < 30; i++) {
sum[rt][i] += sum_[near][i];
cnt[rt][i] += cnt_[near][i];
Ans -= sum_[near][i] * cnt_[near][i^1] << (i>>1);
}
}
for (int i = 0; i < 30; i++)
Ans += sum[rt][i] * cnt[rt][i^1] << (i>>1);
for (int i = h[rt]; i; i = nx[i])
if (!vis[to[i]]) divide(to[i]);
}
void modify(int x, int val) {
for (int i = 0; i < 15; i++) {
register int p0 = f[x][t[x]],
fj = i<<1 | a[x]>>i & 1,
tj = i<<1 | val >>i & 1;
if (fj == tj) continue;
for (int k = 1; k <= t[x]; k++) {
register int p = f[x][k], dis = d[x][k-1];
cnt[p0][fj]--;
cnt_[p][fj]--;
sum[p0][fj] -= dis;
sum_[p][fj] -= dis;
Ans += (cnt[p0][fj] - cnt[p0][tj] - cnt_[p][fj] + cnt_[p][tj]) * dis
+ sum[p0][fj] - sum[p0][tj] - sum_[p][fj] + sum_[p][tj] << i;
cnt[p0][tj]++;
cnt_[p][tj]++;
sum[p0][tj] += dis;
sum_[p][tj] += dis;
p0 = p;
}
}
a[x] = val;
}
bug #1
setroot(near = to[i], root = rt, w[i]);
near
应该是rt
的分治儿子,即下一层分治中心,而不是与near
的连接点to[i]
。因此应该对子树先divide()
,得到子树重心,再setroot()
。
但是这样在setroot()
时会受divide()
设下的vis[]
阻挡。可以在divide()
返回时把自己的vis[]
标记抹去。
bug #2
改过上面这个bug以后,由于setroot()
和divide()
的顺序交换了,所以f[x][0..t[x]]
以前是深度递增的,现在变成深度递减的了。所以关于t[x]
要做一系列调整。
bug #3
我们仔细考虑一下modify()
需要更新的信息:
x
在点分树上所有的祖先要更新。同时更新并扣除这些点向x
方向的子树的内部贡献(带_
的对应数组),即从x
到根除根外的点所记录的数组。
先前漏下了这一部分:最后要更新x
子树内部的信息——这部分即x
与其子树内点之间构成的路径。这里需要更新cnt
,不需改变sum
(因为dis == 0
)。
bug #4
额。。。最后一个bug调了半天。。。
。。然后发现我tm数组开小了。。。
所以我考试的时候是怎么把30000的log算成13的。。。QAQ
Final Result
long long sum[30050][30], sum_[30050][30], Ans;
int cnt[30050][30], cnt_[30050][30];
int f[30050][16], d[30050][16], t[30050];
int root, near;
void setroot(int x, int fa, int dis) {
f[x][++t[x]] = root;
d[x][t[x]] = dis;
for (int i = 0; i < 15; i++) {
register int j = i<<1 | a[x]>>i & 1;
sum_[near][j] += dis;
cnt_[near][j]++;
}
for (int i = h[x], u; i; i = nx[i])
if (!vis[u = to[i]] && u ^ fa)
setroot(u, x, dis+w[i]);
}
int divide(int rt) {
int S = dfssize(rt,0) >>1;
while (max[rt] > S) rt = son[rt];
vis[rt] = true;
f[rt][0] = rt;
d[rt][0] = 0;
for (int i = 0; i < 15; i++)
cnt[rt][i<<1 | a[rt]>>i&1] = 1;
for (int i = h[rt]; i; i = nx[i])
if (!vis[to[i]]) {
near = divide(to[i]);
setroot(to[i], root = rt, w[i]);
for (int i = 0; i < 30; i++) {
sum[rt][i] += sum_[near][i];
cnt[rt][i] += cnt_[near][i];
Ans -= sum_[near][i] * cnt_[near][i^1] << (i>>1);
}
}
for (int i = 0; i < 30; i++)
Ans += sum[rt][i] * cnt[rt][i^1] << (i>>1);
vis[rt] = false;
return rt;
}
void modify(int x, int val) {
for (int i = 0; i < 15; i++) {
register int p0 = f[x][t[x]],
fj = i<<1 | a[x]>>i & 1,
tj = i<<1 | val >>i & 1;
if (fj == tj) continue;
for (int k = t[x]; k > 0; k--) {
register int p = f[x][k-1], dis = d[x][k];
cnt[p0][fj]--;
cnt_[p][fj]--;
sum[p0][fj] -= dis;
sum_[p][fj] -= dis;
Ans += (cnt[p0][fj] - cnt[p0][tj] - cnt_[p][fj] + cnt_[p][tj]) * dis
+ sum[p0][fj] - sum[p0][tj] - sum_[p][fj] + sum_[p][tj] << i;
cnt[p0][tj]++;
cnt_[p][tj]++;
sum[p0][tj] += dis;
sum_[p][tj] += dis;
p0 = p;
}
cnt[x][fj]--;
Ans += sum[x][fj] - sum[x][tj] << i;
cnt[x][tj]++;
}
a[x] = val;
}