推荐博客: http://www.cnblogs.com/Mychael/p/9257242.html

感觉还挺好玩的

首先考虑以1为根,把每一个点子树的权值和都算出来,记为$val_{i}$,那么在所有操作都没有开始的时候(以$1$为根的)$ans_{1} = \sum_{i= 1}^{n}val_{i}^{2}$

考虑到一个修改的操作只会对修改的点$x$到根($1$)链上的点产生影响,那么一次修改只要修对这条树链上的点增加$v - a_{x}$(假设修改后的值为$v$)就好了。

链剖之后线段树维护一下$val_{i}$,区间修改就很简单。

然后考虑换根:

我们发现当以$x$为根的时候,$x$原来的子树显然不会受到影响,而变化了的是原来的根$1$到$x$的链上的点,不妨设有$k$个结点,换根前(以$1$为根)的每个结点子树$val$值和为$a_{i}$,换根后(以$x$为根)的每个结点子树$val$值和为$b_{i}$

有一条显然的性质:$a_{i + 1} + b_{i} = a_{1} = b_{k}$都等于原来全部结点的$val$值和

那么换根之后的答案  $ans_{x} = ans_{1} - \sum_{i = 1}^{k}a_{i}^{2} + \sum_{i = 1}^{k}b_{i}^{2}$

代入上面的那条性质消掉$b$,发现$ans_{x} = ans_{1} + (k - 1)a_{1}^{2} - 2a_{1}\sum_{i = 2}^{k}a_{i}$

设$s_{i}$表示$i$的子树中所有$val$值和,那么$ans_{x} = ans_{1} + s_{1}((k + 1) s_{1} - 2\sum_{i = 1}^{k}s_{i})$。

容易发现这个$k$即为$dep_{x}$,而这个$\sum_{i = 1}^{k}s_{i}$ 和 $s_{1}$显然可以用线段树维护出来

考虑一下, 一次修改还会对$ans_{1}$产生影响,$ans_{1} += \sum_{i = 1}^{tot}(val_{i}+ \Delta v)^{2} - \sum_{i = 1}^{tot}val_{i}^{2} = tot\Delta v^{2} + 2\Delta v\sum_{i = 1}^{tot}val_{i}$。

因为每次发生变化的只有一条树链上的点,所以$tot = dep_{x}$,这个原来的$\sum_{i = 1}^{tot}val_{i}$可以在跳轻重链的过程中算出来。

时间复杂度$O(nlog^{2}n)$。

Code:

#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll; const int N = 2e5 + ; int n, qn, dfsc = , dep[N], siz[N], id[N];
int tot = , head[N], top[N], fa[N], son[N];
ll a[N], ans = 0LL, nowSum = 0LL, w[N], val[N]; struct Edge {
int to, nxt;
} e[N << ]; inline void add(int from, int to) {
e[++tot].to = to;
e[tot].nxt = head[from];
head[from] = tot;
} template <typename T>
inline void read(T &X) {
X = ;
char ch = ;
T op = ;
for(; ch > ''|| ch < ''; ch = getchar())
if(ch == '-') op = -;
for(; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
X *= op;
} void dfs1(int x, int fat, int depth) {
siz[x] = , fa[x] = fat, dep[x] = depth, val[x] = a[x];
int maxson = -;
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fat) continue;
dfs1(y, x, depth + );
siz[x] += siz[y], val[x] += val[y];
if(siz[y] > maxson)
maxson = siz[y], son[x] = y;
}
} void dfs2(int x, int topf) {
w[id[x] = ++dfsc] = val[x], top[x] = topf;
if(!son[x]) return;
dfs2(son[x], topf);
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fa[x] || y == son[x]) continue;
dfs2(y, y);
}
} namespace SegT {
ll s[N << ], tag[N << ]; #define lc p << 1
#define rc p << 1 | 1
#define mid ((l + r) >> 1) inline void up(int p) {
if(p) s[p] = s[lc] + s[rc];
} inline void down(int p, int l, int r) {
if(!tag[p]) return;
s[lc] += 1LL * (mid - l + ) * tag[p];
s[rc] += 1LL * (r - mid) * tag[p];
tag[lc] += tag[p], tag[rc] += tag[p];
tag[p] = 0LL;
} void build(int p, int l, int r) {
tag[p] = 0LL;
if(l == r) {
s[p] = w[l];
return;
} build(lc, l, mid);
build(rc, mid + , r);
up(p);
} void modify(int p, int l, int r, int x, int y, ll v) {
if(x <= l && y >= r) {
s[p] += 1LL * (r - l + ) * v;
tag[p] += v;
return;
} down(p, l, r);
if(x <= mid) modify(lc, l, mid, x, y, v);
if(y > mid) modify(rc, mid + , r, x, y, v);
up(p);
} ll qSum(int p, int l, int r, int x, int y) {
if(x <= l && y >= r) return s[p];
down(p, l, r); ll res = 0LL;
if(x <= mid) res += qSum(lc, l, mid, x, y);
if(y > mid) res += qSum(rc, mid + , r, x, y);
return res;
} } using namespace SegT; inline void mTree(int x) {
ll v, sum = 0LL, len = (ll)dep[x]; read(v);
v -= a[x], a[x] += v;
for(; x != ; x = fa[top[x]]) {
sum += qSum(, , n, id[top[x]], id[x]);
modify(, , n, id[top[x]], id[x], v);
}
ans += 2LL * v * sum + 1LL * v * v * len;
nowSum += v;
} inline ll qTree(int x) {
ll res = 0LL;
for(; x != ; x = fa[top[x]])
res += qSum(, , n, id[top[x]], id[x]);
return res;
} inline void solve(int x) {
ll k = (ll)dep[x], sum = qTree(x);
printf("%lld\n", ans + nowSum * ((k + ) * nowSum - * sum));
} int main() {
read(n), read(qn);
for(int x, y, i = ; i < n; i++) {
read(x), read(y);
add(x, y), add(y, x);
}
for(int i = ; i <= n; i++) read(a[i]); dfs1(, , );
dfs2(, );
build(, , n); /* for(int i = 1; i <= n; i++)
printf("%d ", dep[i]);
printf("\n");
for(int i = 1; i <= n; i++)
printf("%d ", top[i]);
printf("\n");
for(int i = 1; i <= n; i++)
printf("%d ", w[i]);
printf("\n"); */ for(int i = ; i <= n; i++) {
nowSum += a[i];
ans += val[i] * val[i];
}
// printf("%lld\n", ans); for(int op, x; qn--; ) {
read(op), read(x);
if(op == ) mTree(x);
else solve(x);
} return ;
}

Luogu 3676 小清新数据结构题的更多相关文章

  1. 【Luogu3676】小清新数据结构题(动态点分治)

    [Luogu3676]小清新数据结构题(动态点分治) 题面 洛谷 题解 先扯远点,这题我第一次看的时候觉得是一个树链剖分+线段树维护. 做法大概是这样: 我们先以任意一个点为根,把当前点看成是一棵有根 ...

  2. 洛谷 P3676 小清新数据结构题

    https://www.luogu.org/problemnew/show/P3676 这题被我当成动态dp去做了,码了4k,搞了一个换根的动态dp #include<cstdio> #i ...

  3. [Luogu3676]小清新数据结构题

    题面戳我 题意:给一棵树,树上有点权,每次操作为修改一个点的点权,或者是询问以某个点为根时,每棵子树(以每个点为根,就有n棵子树)点权和的平方和. \(n\le2*10^5\),保证答案在long l ...

  4. [P3676]小清新数据结构题

    Description: 给你一棵树,每次询问以一个点为根时所有子树点权和的平方和 带修改 Hint: \(n\le 2*10^5\) Solution: 这题只要推出式子就很简单了 如果不换根这个平 ...

  5. Luogu3676 小清新数据结构题 动态点分治

    传送门 换根类型的统计问题动态点分治都是很好做的. 设所有点的点权和为$sum$ 首先,我们先不考虑求$\sum\limits_i s_i^2$,先考虑如何在换根的情况下求$\sum\limits_i ...

  6. 洛谷P3676 小清新数据结构题(动态点分治+树链剖分)

    传送门 感觉这题做下来心态有点崩……$RMQ$求$LCA$没有树剖快我可以理解为是常数太大……然而我明明用了自以为不会退化的点分然而为什么比会退化的点分跑得反而更慢啊啊啊啊~~~ 先膜一波zsy大佬 ...

  7. [luogu3676] 小清新数据结构题 [树链剖分+线段树]

    题面 传送门 思路 本来以为这道题可以LCT维护子树信息直接做的,后来发现这样会因为splay形态改变影响子树权值平方和,是splay本身的局限性导致的 所以只能另辟蹊径 首先,我们考虑询问点都在1的 ...

  8. 洛谷 P3676 - 小清新数据结构题(动态点分治)

    洛谷题面传送门 题目名称好评(实在是太清新了呢) 首先考虑探究这个"换根操作"有什么性质.我们考虑在换根前后虽然每个点的子树会变,但整棵树的形态不会边,换句话说,割掉每条边后,得到 ...

  9. 洛谷P3676 小清新数据结构题 [动态点分治]

    传送门 思路 这思路好妙啊! 首先很多人都会想到推式子之后树链剖分+线段树,但这样不够优美,不喜欢. 脑洞大开想到这样一个式子: \[ \sum_{x} sum_x(All-sum_x) \] 其中\ ...

随机推荐

  1. New Concept English three (56)

    The river which forms the eastern boundary of our farm has always played an important part in our li ...

  2. FMDB的操作(转),这篇比我写的好

    直接看吧 http://blog.devtang.com/blog/2012/04/22/use-fmdb/

  3. Leetcode 970. Powerful Integers

    Brute Force(暴力) class Solution(object): def powerfulIntegers(self, x, y, bound): """ ...

  4. c++ 修改stl set中的元素

    set的迭代器it有const修饰符,那么对它元素的修改就必然不能成功了.但是有时候遇到要修改stl set元素的问题,这个问题一般的解决方法是先erase这个元素,然后再insert,这样效率很低, ...

  5. VueJs路由跳转——vue-router的使用

    对于单页应用,官方提供了vue-router进行路由跳转的处理,本篇主要也是基于其官方文档写作而成. 安装 基于传统,我更喜欢采用npm包的形式进行安装. npm install vue-router ...

  6. C++中预定义的宏

    以下信息摘自与标准C++的文档中. 如果把这些宏加在程序的日志中,它将为开发人员进行问题分析提供了很好的帮助. standard c++ 1998版The following macro names ...

  7. HIVE-执行hive的几种方式,和把HIVE保存到本地的几种方式

    网上相关教程很多,这里我主要是简单总结下几种常用的方法,方便日后查询. 第一种,在bash中直接通过hive -e命令,并用 > 输出流把执行结果输出到制定文件 hive -e "se ...

  8. BZOJ1202:[HNOI2005]狡猾的商人

    浅谈并查集:https://www.cnblogs.com/AKMer/p/10360090.html 题目传送门:https://lydsy.com/JudgeOnline/problem.php? ...

  9. LeetCode第一题:Two Sum

    Given an array of integers, return indices of the two numbers such that they add up to a specific ta ...

  10. StringBuilder、StringBuffer、String区别

          相信大家对 String 和 StringBuffer 的区别也已经很了解了,但是估计还是会有很多同志对这两个类的工作原理有些不清楚的地方,今天重新把这个概念给大家复习一下,顺便牵出 J2 ...