洛谷传送门
题目描述
在很久很久以前,有一棵 n n n个点的树,每个点有一个点权。
现在有 q q q次操作,每次操作是修改一个点的点权或指定一个点,询问以这个点为根时每棵子树点权和的平方和。
(题目不是很好懂,没看太懂的可以看看样例解释)
输入输出格式
输入格式:
第一行两个整数 n n n、 q q q。
接下来 n − 1 n-1 n−1行每行两个整数 a a a和 b b b,表示树中 a a a与 b b b之间有一条边,保证给出的边不会重复。
接下来一行 n n n个整数,第i个整数表示第 i i i个点的点权。
接下来 q q q行每行两或三个数,如果第一个数为 1 1 1,那么接下来有两个数 x x x和 y y y,表示将第 x x x个点的点权修改为 y y y,如果第一个数为 2 2 2,那么接下来有一个数 x x x,表示询问以x为根时每棵子树点权和的平方和。
输出格式:
对于每个询问输出答案。
输入输出样例
输入样例#1:
4 5
1 2
2 3
2 4
4 3 2 1
2 2
1 1 3
2 3
1 2 4
2 4
输出样例#1:
121
140
194
说明
样例解释
这是一个菊花图, 2 2 2与 1 1 1、 3 3 3、 4 4 4间有边。
一开始每个点点权分别为 4 4 4、 3 3 3、 2 2 2、 1 1 1。
第一个询问以 2 2 2为根, 1 1 1、 3 3 3、 4 4 4子树中都只有本身一个点, 2 2 2子树中有所有点,那么 1 1 1、 3 3 3、 4 4 4子树中的点权和就分别是自己的点权 4 4 4、 2 2 2、 1 1 1, 2 2 2子树中的点权和就是 4 + 3 + 2 + 1 = 10 4+3+2+1=10 4+3+2+1=10, 4 2 4^2 42+ 2 2 2^2 22+ 1 2 1^2 12+ 1 0 2 10^2 102= 121 121 121。
接下来将第一个点点权修改为 3 3 3,每个点点权分别为 3 3 3、 3 3 3、 2 2 2、 1 1 1。
第二个询问以 3 3 3为根, 1 1 1、 4 4 4子树中只有自己, 2 2 2子树中有 1 1 1、 2 2 2、 4 4 4, 3 3 3子树中有所有点, 1 1 1、 4 4 4子树点权和就是自己的点权 3 3 3、 1 1 1, 2 2 2子树点权和就是 3 + 3 + 1 = 7 3+3+1=7 3+3+1=7, 3 3 3子树点权和为 3 + 3 + 2 + 1 = 9 3+3+2+1=9 3+3+2+1=9, 3 2 3^2 32+ 1 2 1^2 12+ 7 2 7^2 72+ 9 2 9^2 92= 140 140 140。
接下来把第二个点点权修改为 4 4 4,每个点点权分别为 3 3 3、 4 4 4、 2 2 2、 1 1 1。
第三个询问以 4 4 4为根, 1 1 1、 3 3 3子树点权和就是 3 3 3和 2 2 2, 2 2 2子树点权和就是 3 + 4 + 2 = 9 3+4+2=9 3+4+2=9, 4 4 4子树点权和为 3 + 4 + 2 + 1 = 10 3+4+2+1=10 3+4+2+1=10, 3 2 + 2 2 + 9 2 + 1 0 2 = 194 3^2+2^2+9^2+10^2=194 32+22+92+102=194。
数据范围
对于10%的数据, n , q ≤ 2000 n,q \leq 2000 n,q≤2000。
对于40%的数据, n , q ≤ 60000 n,q \leq 60000 n,q≤60000。
对于另外30%的数据,保证每次询问的根都为 1 1 1。
对于100%的数据, 1 ≤ n , q ≤ 200000 1 \leq n,q \leq 200000 1≤n,q≤200000, − 10 ≤ -10 \leq −10≤输入的每个点权 ≤ 10 \leq 10 ≤10。
建议使用输入优化~~,虽然标程没加读入优化也能过~~
解题分析
真的是一道小清新数据结构题, 并没有什么毒瘤的数据结构套数据结构的情况。
考虑
30
p
t
s
30pts
30pts的情况, 我们每次根都在
1
1
1号点, 也只需要维护每次修改后答案即可。 考虑修改的位置为
u
u
u, 实际有影响的子树权值和只有处于
u
u
u点和
u
u
u上方的点, 设处于
1
→
u
1\to u
1→u号点路径上的点依次是
1
,
2
,
3
,
.
.
.
,
u
1,2,3,...,u
1,2,3,...,u, 修改的
Δ
\Delta
Δ值为
d
e
l
del
del, 那么现在的答案就是。
∑
i
=
1
u
(
s
u
m
[
i
]
+
d
e
l
)
2
−
∑
i
=
1
u
s
u
m
[
i
]
2
=
u
×
d
e
l
2
+
2
×
(
∑
i
=
1
u
)
×
s
u
m
[
i
]
×
d
e
l
\sum_{i=1}^{u}(sum[i]+del)^2-\sum_{i=1}^{u}sum[i]^2 \\ =u\times del^2+2\times (\sum_{i=1}^u)\times sum[i]\times del
i=1∑u(sum[i]+del)2−i=1∑usum[i]2=u×del2+2×(i=1∑u)×sum[i]×del
于是我们预处理出每个点的子树和, 然后链上查询和就好了。
我们再考虑如果根不是
1
1
1而是
v
v
v的情况, 实质上对答案有影响的也只有
1
→
v
1\to v
1→v这条链上的点, 设
s
[
i
]
s[i]
s[i]为以
v
v
v为根的子树权值和,
1
→
u
1\to u
1→u号点路径上经过的点分别是
1
,
2
,
3
,
.
.
.
,
1,2,3,...,
1,2,3,...,, 可以发v现
s
u
m
[
i
+
1
]
+
s
[
i
]
=
∑
v
a
l
[
i
]
sum[i+1]+s[i]=\sum_{val[i]}
sum[i+1]+s[i]=∑val[i], 而
s
[
u
]
=
s
u
m
[
1
]
s[u]=sum[1]
s[u]=sum[1], 设
t
o
t
=
∑
v
a
l
[
i
]
tot=\sum_{val[i]}
tot=∑val[i]那么现在的答案就是:
a
n
s
′
=
a
n
s
+
∑
i
=
1
v
−
1
s
[
i
]
−
∑
i
=
2
v
s
u
m
[
i
]
=
a
n
s
+
∑
i
=
2
v
(
t
o
t
−
s
u
m
[
i
]
)
2
−
∑
i
=
2
v
s
u
m
[
i
]
2
=
a
n
s
+
(
v
−
1
)
×
t
o
t
2
−
2
×
t
o
t
×
∑
i
=
2
v
s
u
m
[
i
]
=
a
n
s
+
(
v
+
1
)
×
t
o
t
2
−
2
×
t
o
t
×
∑
i
=
1
v
s
u
m
[
i
]
ans'=ans+\sum_{i=1}^{v-1}s[i]-\sum_{i=2}^{v}sum[i] \\ =ans+\sum_{i=2}^{v}(tot-sum[i])^2-\sum_{i=2}^{v}sum[i]^2 \\ =ans+(v-1)\times tot^2-2\times tot\times \sum_{i=2}^{v} sum[i] \\ =ans+(v+1)\times tot^2-2\times tot\times \sum_{i=1}^{v}sum[i]
ans′=ans+i=1∑v−1s[i]−i=2∑vsum[i]=ans+i=2∑v(tot−sum[i])2−i=2∑vsum[i]2=ans+(v−1)×tot2−2×tot×i=2∑vsum[i]=ans+(v+1)×tot2−2×tot×i=1∑vsum[i]
同样一个链上和就好了。
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MX 200500
#define ls tree[now].son[0]
#define rs tree[now].son[1]
#define dad tree[now].fat
bool neg;
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc)
if(c == '-') neg = true;
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
if(neg) x = -x, neg = false;
}
int sta[MX], head[MX], val[MX];
int dot, q, cnt, top;
ll ans;
struct Edge {int to, nex;} edge[MX << 1];
struct Node {int son[2], fat, val, siz, tag; ll sub, sum; bool rev;} tree[MX];
IN void add(R int from, R int to) {edge[++cnt] = {to, head[from]}, head[from] = cnt;}
void DFS (R int now, R int fa)
{
tree[now].sum = tree[now].val;
for (R int i = head[now]; i; i = edge[i].nex)
{
if(edge[i].to ^ fa)
{
tree[edge[i].to].fat = now, DFS(edge[i].to, now);
tree[now].sum += tree[edge[i].to].sum;
}
}
ans += tree[now].sum * tree[now].sum;
tree[now].sub = tree[now].sum;
}
namespace LCT
{
IN bool get(R int now) {return tree[dad].son[1] == now;}
IN bool nroot(R int now) {return tree[dad].son[1] == now || tree[dad].son[0] == now;}
IN void pushrev(R int now) {std::swap(ls, rs), tree[now].rev ^= 1;}
IN void pushdown(R int now)
{
if(tree[now].rev) pushrev(ls), pushrev(rs), tree[now].rev = 0;
if(tree[now].tag)
{
if(ls) tree[ls].sum += tree[ls].siz * tree[now].tag, tree[ls].tag += tree[now].tag, tree[ls].sub += tree[now].tag;
if(rs) tree[rs].sum += tree[rs].siz * tree[now].tag, tree[rs].tag += tree[now].tag, tree[rs].sub += tree[now].tag;
tree[now].tag = 0;
}
}
IN void pushup(R int now)
{
tree[now].siz = tree[ls].siz + tree[rs].siz + 1;
tree[now].sum = tree[ls].sum + tree[rs].sum + tree[now].sub;
}
IN void rotate(R int now)
{
R int fa = dad, grand = tree[fa].fat;
R bool dir = get(now);
tree[fa].son[dir] = tree[now].son[dir ^ 1];
tree[tree[now].son[dir ^ 1]].fat = fa;
tree[now].fat = grand;
if(nroot(fa)) tree[grand].son[get(fa)] = now;
tree[now].son[dir ^ 1] = fa;
tree[fa].fat = now;
pushup(fa);
}
IN void splay(R int now)
{
int tmp, fa;
sta[top = 1] = tmp = now;
W (nroot(now)) sta[++top] = now = dad;
W (top) pushdown(sta[top--]); now = tmp;
W (nroot(now))
{
fa = dad;
if(nroot(fa)) rotate(get(now) == get(fa) ? fa : now);
rotate(now);
}
pushup(now);
}
IN void access(R int now)
{
for(R int x = 0; now; x = now, now = dad)
splay(now), rs = x, pushup(now);
}
IN void makeroot(R int now) {access(now), splay(now), pushrev(now);}
IN void split(R int x, R int y)
{
makeroot(x);
access(y);
splay(y);
}
IN void modify(R int pos, R int del)
{
split(pos, 1); tree[1].sub += del;
ans += del * del * tree[1].siz + 2 * del * tree[1].sum;
tree[1].sum += tree[1].siz * del;
tree[1].tag += del;
}
IN void query(R int pos)
{
split(pos, 1);
printf("%lld\n", ans + (tree[1].siz + 1) * tree[1].sub * tree[1].sub - 2 * tree[1].sub * tree[1].sum);
}
}
int main(void)
{
int a, b, opt;
in(dot), in(q);
for (R int i = 1; i < dot; ++i) in(a), in(b), add(a, b), add(b, a);
for (R int i = 1; i <= dot; ++i)
in(tree[i].val), val[i] = tree[i].val;
DFS(1, 0);
W (q--)
{
in(opt), in(a);
if(opt & 1) in(b), LCT::modify(a, b - val[a]), val[a] = b;
else LCT::query(a);
}
}