题面
题解
先来一发很显然的暴力
维护两个数组,一个是子树的val和,一个是子树的val和的平方和
暴力更新,暴力查询就可以获得10分吐槽一波luogu的部分分
# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
# define Sqr(x) ((x) * (x))
using namespace std;
typedef long long ll;
const int _(2e5 + 10);
IL ll Read(){
RG ll x = 0, z = 1; RG char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return x * z;
}
int n, q, fst[_], nxt[_ << 1], to[_ << 1], cnt, val[_], sum[_], f[_], fa[_], S[_], top, son[_];
IL void Add(RG int u, RG int v){ to[cnt] = v; nxt[cnt] = fst[u]; fst[u] = cnt++; }
IL void Dfs(RG int u, RG int ff){
fa[u] = ff; sum[u] = val[u];
for(RG int e = fst[u]; e != -1; e = nxt[e]){
if(to[e] == ff) continue;
Dfs(to[e], u);
sum[u] += sum[to[e]]; f[u] += f[to[e]];
}
f[u] += Sqr(sum[u]);
}
IL void Modify(RG int u, RG int d){
top = son[u] = 0;
for(RG int v = u; fa[v]; v = fa[v]) S[++top] = fa[v], son[fa[v]] = v;
for(RG int i = top; i; --i) f[S[i]] -= f[son[S[i]]] + Sqr(sum[S[i]]), sum[S[i]] -= sum[son[S[i]]];
f[u] -= Sqr(sum[u]); sum[u] -= val[u]; val[u] = d; sum[u] += val[u]; f[u] += Sqr(sum[u]);
for(RG int i = 1; i <= top; ++i) sum[S[i]] += sum[son[S[i]]], f[S[i]] += f[son[S[i]]] + Sqr(sum[S[i]]);
}
IL int Query(RG int u){
S[top = 1] = u; son[u] = 0; RG int ss = 0, ff = 0;
for(RG int v = u; fa[v]; v = fa[v]) S[++top] = fa[v], son[fa[v]] = v;
for(RG int i = top; i; --i){
ss += sum[S[i]] - sum[son[S[i]]];
ff += f[S[i]] - f[son[S[i]]] - Sqr(sum[S[i]]) + Sqr(ss);
}
return ff;
}
int main(RG int argc, RG char* argv[]){
n = Read(); q = Read(); Fill(fst, -1);
for(RG int i = 1, x, y; i < n; ++i) x = Read(), y = Read(), Add(x, y), Add(y, x);
for(RG int i = 1; i <= n; ++i) val[i] = Read();
Dfs(1, 0);
while(q--){
RG int op = Read(), x = Read(), y;
if(op == 1) y = Read(), Modify(x, y);
else printf("%d\n", Query(x));
}
return 0;
}
考虑优化
看暴力:每次修改只会影响到它到1的链,询问也是它到1的链
那么考虑用树链剖分变成dfs序
链剖都想了那就考虑用线段树维护这个dfs序上的信息
维护dfs序上每个点子树的val和的平方和
修改?
假设修改\(u\)的变化量为\(d\)
那么该完后\(u\)的祖先们\(i\)的子树平方和变成
\(\sum_{j\in i的子树}(sum[j]+d)^2=\sum(sum^2[j]+2*d*sum[j]+d^2)\)
设有子树大小为\(num\),就是\(\sum(sum^2[j])+2*d\sum sum[j] + num*d^2\)
那么我们只需要再维护每个点子树的val和的和就可以+lazy修改了
每个点子树的val和的和也很好弄直接区间加lazy就好
查询?
S[top = 1] = u; son[u] = 0; RG int ss = 0, ff = 0;
for(RG int v = u; fa[v]; v = fa[v]) S[++top] = fa[v], son[fa[v]] = v;
for(RG int i = top; i; --i){
ss += sum[S[i]] - sum[son[S[i]]];
ff += f[S[i]] - f[son[S[i]]] - Sqr(sum[S[i]]) + Sqr(ss);
}
return ff;
根据暴力推一下,\(ss,ff\)又减又加的,最后会消除一些
设根为1的答案为\(ans1\),当前询问的点到根的路径上有\(x\)个点,分别标成\(u_1到u_x,u_1就是1,u_x就是当前点\),\(sum[i]表示i子树的val和\)
那么\(ans=ans1-\sum_{i=1}^{x}sum^2[u_i]+\sum_{i=2}^{x}(sum[u_1]-sum[u_i])\)(很好推的)
化简一下
\(ans=ans1-sum^2[u_1]*(x-1)-2*sum[u_1]\sum_{i=2}^{x}sum[u_i]\)
\(sum\)和线段树+链剖查询就好了
\(sum[u_1]即sum[1]\)就是整个数的val和可以单点查询也可维护全局变量
# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
# define Sqr(x) ((x) * (x))
using namespace std;
typedef long long ll;
const int _(2e5 + 10);
IL ll Read(){
RG ll x = 0, z = 1; RG char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return x * z;
}
int n, q, fst[_], nxt[_ << 1], to[_ << 1], cnt, val[_];
int size[_], son[_], top[_], dfn[_], deep[_], fa[_], Index, id[_];
ll sums[_ << 2], sumq[_ << 2], tag[_ << 2], sum[_];
IL void Add(RG int u, RG int v){ to[cnt] = v; nxt[cnt] = fst[u]; fst[u] = cnt++; }
IL void Dfs1(RG int u, RG int ff){
fa[u] = ff; deep[u] = deep[ff] + 1; sum[u] = val[u]; size[u] = 1;
for(RG int e = fst[u]; e != -1; e = nxt[e]){
if(to[e] == ff) continue;
Dfs1(to[e], u);
size[u] += size[to[e]]; sum[u] += sum[to[e]];
if(size[to[e]] > size[son[u]]) son[u] = to[e];
}
}
IL void Dfs2(RG int u, RG int Top){
dfn[u] = ++Index; top[u] = Top; id[Index] = u;
if(son[u]) Dfs2(son[u], Top);
for(RG int e = fst[u]; e != -1; e = nxt[e]) if(!dfn[to[e]]) Dfs2(to[e], to[e]);
}
IL void Build(RG int x, RG int l, RG int r){
if(l == r){ sums[x] = sum[id[l]]; sumq[x] = Sqr(sum[id[l]]); return; }
RG int mid = (l + r) >> 1;
Build(x << 1, l, mid); Build(x << 1 | 1, mid + 1, r);
sums[x] = sums[x << 1] + sums[x << 1 | 1]; sumq[x] = sumq[x << 1] + sumq[x << 1 | 1];
}
IL void Adjust(RG int x, RG ll len, RG ll d){ tag[x] += d; sumq[x] += 2 * sums[x] * d + Sqr(d) * len; sums[x] += d * len; }
IL void Pushdown(RG int x, RG int l, RG int mid, RG int r){ if(!tag[x]) return; Adjust(x << 1, mid - l + 1, tag[x]); Adjust(x << 1 | 1, r - mid, tag[x]); tag[x] = 0; }
IL void Modify(RG int x, RG int l, RG int r, RG int L, RG int R, RG ll d){
if(L <= l && R >= r){ Adjust(x, r - l + 1, d); return; }
RG int mid = (l + r) >> 1;
Pushdown(x, l, mid, r);
if(L <= mid) Modify(x << 1, l, mid, L, R, d);
if(R > mid) Modify(x << 1 | 1, mid + 1, r, L, R, d);
sums[x] = sums[x << 1] + sums[x << 1 | 1]; sumq[x] = sumq[x << 1] + sumq[x << 1 | 1];
}
IL ll Query(RG int x, RG int l, RG int r, RG int L, RG int R){
if(L <= l && R >= r) return sums[x];
RG int mid = (l + r) >> 1; RG ll ans = 0;
Pushdown(x, l, mid, r);
if(L <= mid) ans = Query(x << 1, l, mid, L, R);
if(R > mid) ans += Query(x << 1 | 1, mid + 1, r, L, R);
sums[x] = sums[x << 1] + sums[x << 1 | 1]; sumq[x] = sumq[x << 1] + sumq[x << 1 | 1];
return ans;
}
IL void Pre_Modify(RG int u, RG ll d){
while(top[u] ^ top[1]) Modify(1, 1, n, dfn[top[u]], dfn[u], d), u = fa[top[u]];
Modify(1, 1, n, 1, dfn[u], d);
}
IL ll Calc(RG int u){
RG ll num = deep[u] - deep[1], sum_1 = Query(1, 1, n, 1, 1), An1 = 0;
while(top[u] ^ top[1]) An1 += Query(1, 1, n, dfn[top[u]], dfn[u]), u = fa[top[u]];
if(dfn[u] > 1) An1 += Query(1, 1, n, 2, dfn[u]);
return sumq[1] + sum_1 * (sum_1 * num - 2 * An1);
}
int main(RG int argc, RG char* argv[]){
n = Read(); q = Read(); Fill(fst, -1);
for(RG int i = 1, x, y; i < n; ++i) x = Read(), y = Read(), Add(x, y), Add(y, x);
for(RG int i = 1; i <= n; ++i) val[i] = Read();
Dfs1(1, 0); Dfs2(1, 1);
Build(1, 1, n);
while(q--){
RG int op = Read(), x = Read(), y;
if(op == 1) y = Read(), Pre_Modify(x, y - val[x]), val[x] = y;
else printf("%lld\n", Calc(x));
}
return 0;
}