传送门


换根类型的统计问题动态点分治都是很好做的。

设所有点的点权和为$sum$

首先,我们先不考虑求$\sum\limits_i s_i^2$,先考虑如何在换根的情况下求$\sum\limits_i s_i$。

考虑一个点$i$会被统计多少次,显然是$dep_i+1$,那么$\sum\limits_i s_i = \sum\limits_i (dep_i+1) \times val_i = \sum\limits_i dep_i \times val_i + sum$。

$\sum\limits_i dep_i \times val_i$是不是似曾相识……其实就是幻想乡战略游戏

接着我们考虑这个烦人的平方项。那么我们还需要推一个结论:换根不会影响$W=\sum\limits_i s_i \times (sum - s_i)$的值。

证明如下:

我们考虑$K = \sum\limits_i \sum\limits_j val_i \times val_j \times dis(i,j)$,意思就是取两个点,将中间的所有边设为两边的点的权值之积然后相加。显然这一个值是不会因为根的变化而改变的。

接着我们考虑一条边$(x,y)$对$K$的贡献,显然是这条边分割开来的两个子树的权值和的乘积,而无论根是哪一个,这两个子树中必定有且仅有一个是树的一个子树,假设$x$对应的树是子树,它就代表着$s_x \times (sum - s_x)$,把所有边的贡献加起来就是$W=\sum\limits_i s_i \times (sum - s_i)=\sum\limits_i \sum\limits_j val_i \times val_j \times dis(i,j)$,所以$W$是不会因为根的变化而变化的。

而$W=\sum\limits_i s_i \times (sum - s_i) = sum \times \sum\limits_i s_i - \sum\limits_i s_i^2$,那么$\sum\limits_i s_i^2 = sum \times \sum\limits_i s_i - W$

接着我们考虑修改点权对$W$的影响。由$\Delta W = \Delta v \times \sum\limits_j val_j \times dep_j$,实质跟上面计算$\sum\limits_i s_i$的方法一样。

 #include<bits/stdc++.h>
 #define int long long
 //This code is written by Itst
 using namespace std;

 inline int read(){
     ;
     ;
     char c = getchar();
     while(c != EOF && !isdigit(c)){
         if(c == '-')
             f = ;
         c = getchar();
     }
     while(c != EOF && isdigit(c)){
         a = (a << ) + (a << ) + (c ^ ');
         c = getchar();
     }
     return f ? -a : a;
 }

 ;
 struct Edge{
     int end , upEd;
 }Ed[MAXN << ];
 ] , dis[MAXN][] , dep[MAXN];
 ][MAXN << ] , fir[MAXN] , logg2[MAXN << ] , size[MAXN] , cur[MAXN] , up[MAXN] , sum[MAXN];
 int cntEd , N , M , minSize , nowSize , minInd , cntST , W , allV;
 bool vis[MAXN];

 inline void addEd(int a , int b){
     Ed[++cntEd].end = b;
     Ed[cntEd].upEd = head[a];
     head[a] = cntEd;
 }

 void init_dfs(int x , int p){
     dep[x] = dep[p] + ;
     fir[x] = ++cntST;
     sum[x] = val[x];
     ST[][cntST] = x;
     for(int i = head[x] ; i ; i = Ed[i].upEd)
         if(Ed[i].end != p){
             init_dfs(Ed[i].end , x);
             ST[][++cntST] = x;
             sum[x] += sum[Ed[i].end];
         }
     W += sum[x] * (allV - sum[x]);
 }

 inline int cmp(int a , int b){
     return dep[a] < dep[b] ? a : b;
 }

 void init_st(){
      ; i <= cntST ; ++i)
         logg2[i] = logg2[i >> ] + ;
      ;  << i <= cntST ; ++i)
          ; j + ( << i) -  <= cntST ; ++j)
             ST[i][j] = cmp(ST[i - ][j] , ST[i - ][j + ( << (i - ))]);
 }

 inline int LCA(int x , int y){
     x = fir[x];
     y = fir[y];
     if(x > y)
         swap(x , y);
     ];
      << t) + ]);
 }

 inline int calcLen(int x , int y){
     );
 }

 void getSize(int x){
     vis[x] = ;
     ++nowSize;
     for(int i = head[x] ; i ; i = Ed[i].upEd)
         if(!vis[Ed[i].end])
             getSize(Ed[i].end);
     vis[x] = ;
 }

 void getRoot(int x){
     vis[x] = size[x] = ;
     ;
     for(int i = head[x] ; i ; i = Ed[i].upEd)
         if(!vis[Ed[i].end]){
             getRoot(Ed[i].end);
             maxN = max(maxN , size[Ed[i].end]);
             size[x] += size[Ed[i].end];
         }
     maxN = max(maxN , nowSize - size[x]);
     if(maxN < minSize){
         minSize = maxN;
         minInd = x;
     }
     vis[x] = ;
 }

 void init_dfz(int x , int p){
     nowSize = ;
     minSize = 0x7fffffff;
     getSize(x);
     getRoot(x);
     x = minInd;
     fa[x][] = x;
     fa[x][] = p;
     vis[x] = ;
     sum[x] = val[x];
      ; fa[x][i] ; ++i){
         fa[x][i + ] = fa[fa[x][i]][];
         dis[x][i] = calcLen(fa[x][i] , x);
         up[fa[x][i - ]] += dis[x][i] * val[x];
         sum[fa[x][i]] += val[x];
         cur[fa[x][i]] += dis[x][i] * val[x];
     }
     for(int i = head[x] ; i ; i = Ed[i].upEd)
         if(!vis[Ed[i].end])
             init_dfz(Ed[i].end , x);
 }

 void init(){
     init_dfs( , );
     init_st();
     init_dfz( , );
 }

 inline int query(int x){
     int all = cur[x];
      ; fa[x][i] ; ++i){
         all += cur[fa[x][i]] - up[fa[x][i - ]];
         all += (sum[fa[x][i]] - sum[fa[x][i - ]]) * dis[x][i];
     }
     return all;
 }

 inline void modify(int x , int v){
     W += (v - val[x]) * query(x);
     allV += v - val[x];
     sum[x] += v - val[x];
      ; fa[x][i] ; ++i){
         up[fa[x][i - ]] += (v - val[x]) * dis[x][i];
         cur[fa[x][i]] += (v - val[x]) * dis[x][i];
         sum[fa[x][i]] += v - val[x];
     }
     val[x] = v;
 }

 signed main(){
 #ifndef ONLINE_JUDGE
     freopen("3676.in" , "r" , stdin);
     freopen("3676.out" , "w" , stdout);
 #endif
     N = read();
     M = read();
      ; i < N ; ++i){
         int a = read() , b = read();
         addEd(a , b);
         addEd(b , a);
     }
      ; i <= N ; ++i){
         val[i] = read();
         allV += val[i];
     }
     init();
      ; i <= M ; ++i)
         ){
             int x = read() , y = read();
             modify(x , y);
         }
         else
             cout << allV * (query(read()) + allV) - W << '\n';
     ;
 }

Luogu3676 小清新数据结构题 动态点分治的更多相关文章

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

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

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

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

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

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

  4. Luogu3676 小清新数据结构题(树链剖分+线段树)

    先不考虑换根.考虑修改某个点权值对答案的影响.显然这只会改变其祖先的子树权值和,设某祖先原子树权值和为s,修改后权值增加了x,则对答案的影响为(s+x)2-s2=2sx+x2.可以发现只要维护每个点到 ...

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

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

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

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

  7. 【刷题】洛谷 P3676 小清新数据结构题

    题目背景 本题时限2s,内存限制256M 题目描述 在很久很久以前,有一棵n个点的树,每个点有一个点权. 现在有q次操作,每次操作是修改一个点的点权或指定一个点,询问以这个点为根时每棵子树点权和的平方 ...

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

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

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

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

随机推荐

  1. [VUE ERROR] Invalid prop: type check failed for prop "list". Expected Array, got Undefined

    错误原因: 子组件 props -> list 要求接收的数据类型是 Array, 然而实际接收到的是 Undefined. 子组件代码: props: { list: { type: Arra ...

  2. input 输入框 change 事件和 blur 事件

    输入框的 change 和 blur  事件绝大多数情况下表现是一致的,输入结束后离开输入框会先后触发 change 和 blur.那么这两个事件的区别在哪呢? 当文本框获得焦点后,没有输入任何内容, ...

  3. Mybatis PageHelper 简单使用

    流程 1,maven 依赖 2,在 mybatis 配置文件启用插件 3,修改 service 层 依赖 <!-- https://mvnrepository.com/artifact/com. ...

  4. C# 插件式开发

    在网上找了下插件式编程的资料,这里自己先借鉴下别人的,同时发现有自己的看法,不过由于本人水平有限,不一定有参考价值,写出来一方面是为了总结自己,以求提高,另一方面也希望各为朋友看到我的不足,给我提出宝 ...

  5. linux上部署engineercms、docker和onlyoffice实现文档协作

    等了好久,这次终于下决心在局域网部署了linux系统,并安装docker和load了onlyoffice,利用engineercms进行资料管理和文档协作. 我整理了完整文档,见我的网盘. engin ...

  6. js调用android本地java代码

    js调用android本地java代码 当在Android上使用WebView控件开发一个Web应用时,可以创建一个通过Javascript调用Android端java代码的接口.也就是可以通过Jav ...

  7. [20170615]执行dbms_sqldiag.dump_trace看执行计划.txt

    [20170615]执行dbms_sqldiag.dump_trace看执行计划.txt --//上午在想查看10053执行计划时使用包时出现如下提示: SCOTT@book> @ &r ...

  8. 选择is或者as操作符而不是做强制类型转换

    无论何时,正确选择使用as运算符进行类型转换.比盲目的强制类型转换更安全,而且在运行时效率更高. 用as和is进行转换时,并不是对所有用户定义的类型都能完成,只是在运行时类型和目标类型匹配时,转换才能 ...

  9. 团队项目管理:Github项目误删恢复记录

    参考: 准备更换git托管,如何迁移原git仓库 Github项目误删恢复记录 今天正常上线打卡,发现组织的线上Github仓库被误删了..本来是一场悲剧,所幸在本地的垃圾箱中翻出了还没有删除的本地仓 ...

  10. ScheduledThreadPoolExecutor源码解读

    1. 背景 在之前的博文--ThreadPoolExecutor源码解读已经对ThreadPoolExecutor的实现原理与源码进行了分析.ScheduledExecutorService也是我们在 ...