ZR1050

http://www.zhengruioi.com/problem/1030

题目大意:

给定一棵带点权的树,求所有联通块的点权和的平方的和 \(n \le 10^5\)

题解

首先,关于平方的和或者和的平方我们一般都是考虑新加进来一个点会产生什么样的贡献

比如这一题,我们考虑合并两个集合的代价会发生什么样的变化

首先,设\(a,b\)分别为合并的两个集合的点权和

那么由$a^2 \(变为\)(a + b) ^ 2$的区别是代价多了个\(2ab\)和\(b^2\)

也就是说我们在DP的过程中需要维护所有联通块的点权和进行转移

接下,直接暴力计算是不可以的,我们需要对所有的联通块统一计算,发现对于合并每一对联通块的代价都是上面的东西

乘法分配律之后

我们发现还要额外记录当前点为根的联通块个数

设\(f_i\)表示以\(i\)点为根的联通块的个数

设\(g_i\)表示\(i\)点为根的所有联通块的点权和

设\(h_i\)表示以\(i\)点为根的所有联通块的点权和的平方的和

那么首先根据上面,我们在树形DP的时候可以合并两个联通块

\[h_x = (h_x + g_x\times f_y + 2\times g _x\times g_y+g_y\times f_x)
\]

首先本来原有的联通块的代价肯定是要保留,另外,对于\(h_x\)可以被合并\(f_y\)次,同理\(h_y\)会被合并\(f_y\)次

最后由于\(g\)数组记录的本来就是总贡献,因此直接合并即可

同理我们可以得到\(g\)的转移

\[g_x = (g_x + g_x\times f_y +g_y\times f_x)
\]

最后更新答案即可

总结

对于这种和联通块有关的题目一般可以通过单独算贡献或者DP的方式解决

特别是和平方相关的,要考虑新加进来的数的贡献

代码

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#include<set>
#include<map>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 2e5 + 3;
const LL mod = 998244353;
int n;
LL w[N];
LL f[N],g[N],h[N];
vector <int> G[N];
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline void dfs(int x,int fa){//h:平方的和
f[x] = 1;g[x] = w[x];
h[x] = w[x] * w[x] % mod;
for(int i = 0;i < (int)G[x].size();++i){
int y = G[x][i];
if(y == fa) continue;
dfs(y,x);
h[x] = (h[x] + h[x] * f[y] % mod + 2 * g[x] * g[y] % mod + h[y] * f[x] % mod) % mod;
g[x] = (g[x] + g[x] * f[y] + g[y] * f[x]) % mod;
f[x] = (f[x] + f[x] * f[y]) % mod;
}
}
int main(){
n = read();
for(int i = 1;i <= n;++i) w[i] = read();
for(int i = 1;i < n;++i){
int x = read(),y = read();
G[x].push_back(y);
G[y].push_back(x);
}
dfs(1,0);
LL ans = 0;
for(int i = 1;i <= n;++i) ans = (ans + h[i]) % mod;
cout << ans;
return 0;
}

ZR1050的更多相关文章

随机推荐

  1. qbao

    # -*- coding: utf-8 -*- import Image, cStringIO, webbrowser, re, time, math import urllib, urllib2, ...

  2. HDU 4217

    点击打开题目链接 题型就是数据结构.给一个数组,然后又k次操作,每次操作给定一个数ki, 从数组中删除第ki小的数,要求的是k次操作之后被删除的所有的数字的和. 简单的思路就是,用1标记该数没有被删除 ...

  3. 轻松搞定word中让人抓狂的自动编号

    在word中使用自动编号时,如果一级编号是2,想让其后面的二级编号自动编号为2.1.2.2--,三级编号自动编号为2.1.1.2.1.2--:且在该一级编号调整为3时,后面的二级编号和三级编号的第一位 ...

  4. 2013年山东省第四届ACM大学生程序设计竞赛J题:Contest Print Server

    题目描述     In ACM/ICPC on-site contests ,3 students share 1 computer,so you can print your source code ...

  5. 【算法】leetcode之 Palindrome Partitioning I&II(转载)

    1 Palindrome Partitioning 问题来源:Palindrome Partitioning 该问题简单来说就是给定一个字符串,将字符串分成多个部分,满足每一部分都是回文串,请输出所有 ...

  6. UVa 10502【dp】

    uva 10502 题意:给定01矩阵,求有多少个由1构成的矩阵.1本事也算一个矩阵. 到最后还是想不出来..... 每次枚举两行,从第i行到第j行,k枚举矩阵的宽(column).这样就相当于每次看 ...

  7. 【转】solr deltaImportQuery deltaQuery parentDeltaQuery 用法规则

    solr deltaImportQuery deltaQuery parentDeltaQuery 用法规则 by 建良 · 2013 年 6 月 20 日 query是获取全部数据的SQL delt ...

  8. 阿里云POLARDB如何助力轻松筹打造5亿用户信赖的大病筹款平台?

    轻松筹首创了“大病救助”模式,帮助了众多病患在第一时间解決了医疗资金等问题,为了从源头解决了医疗资金问题.而在轻松筹这样全球5.5亿用户信赖的大病筹款平台的背后,是日益增长的各种数据.面对这样数据量所 ...

  9. 交互式计算和开发环境:IPython

  10. poj 1092 Farmland (Geometry)

    1092 -- Farmland 怎么最近做几何题都这么蛋疼,提交C++过不了交G++就过了.据我估计,原因是用了atan2这个函数,或者是其他一些函数造成了精度的影响.不管怎样,这题最后还是过了~ ...