虽说这题看大家都改得好快啊,但是为什么我感觉这题挺难。(我好菜啊)

所以不管怎么说那群切掉这题的大佬是不会看这篇博客的所以我要开始自嗨了。

这题,明显是树dp啊。只不过出题人想看你发疯,询问二合一了而已。

对于给出了a数组要求b数组的询问,想象一下怎么求。

你先yy一棵树,我懒得画了。。。父节点叫fa,子节点叫s

那么想一想对于s来说它的答案来自与哪里。

首先是它的子树,设以s为根的子树的a值和为w[s],而子树对它的总贡献是son[s]

那么这样理解:所有子树里的点都需要先走到s的直接儿子们,然后再从直接儿子上走一步到s

这样的话,son[s]就成为了b[s]的答案的一部分

  1. void dfs(cri p,cri fa){
  2. w[p]=a[p];
  3. for(int i=fir[p];i;i=l[i])if(to[i]!=fa)
  4. dfs(to[i],p),w[p]+=w[to[i]],son[p]+=son[to[i]]+w[to[i]];
  5. b[p]=son[p];
  6. }

然后考虑完子树的贡献以后,就是子树以外的部分了。

根节点的b数组已经处理出来了,因为根的全部答案都来自于它的子树。

然后我们从上往下dfs,假定我们已经知道了b[fa],我们需要知道b[s]

当时在上一个dfs时这个儿子给父亲的b的贡献是son[s]+w[s]。除去这些贡献,剩下的都是这棵子树以外的贡献了。

这个值代表什么呢?就是从s子树以外的点走到fa需要的总费用啦。

我们现在需要的是从s子树以外的所有点走到s的费用,加上son[s]就是b[s]了。

那么就比较明显了,从fa再走一步到s即可,付出的代价就是子树以外所有点的a值和,即w[1]-w[s](假定从1为整棵树开始dfs)

  1. void Dfs(cri p,cri fa){
  2. for(int i=fir[p];i;i=l[i])if(to[i]!=fa)
  3. b[to[i]]+=b[p]-son[to[i]]-w[to[i]]+w[]-w[to[i]],Dfs(to[i],p);
  4. }

这样的话我们就十分顺利的求解出了b数组。

接下来需要用b数组求解a数组。

正解不太好想,还是先讲最普通的高斯消元。

暗中观察数据范围,所有t=1的点它的n都不大,可以高斯消元。

我们n2求出所有点对的距离(n遍dfs),构造距离方程组,带入b数组,高斯消元求解。

  1. #include<cstdio>
  2. #define cri const register int
  3. int T,t,n,a[],b[],cnt;double dt[][];
  4. int son[],fir[],l[],to[],w[];
  5. void connect(cri a,cri b){
  6. l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;
  7. l[++cnt]=fir[b];fir[b]=cnt;to[cnt]=a;
  8. }
  9. void dfs(cri p,cri fa){
  10. w[p]=a[p];
  11. for(int i=fir[p];i;i=l[i])if(to[i]!=fa)
  12. dfs(to[i],p),w[p]+=w[to[i]],son[p]+=son[to[i]]+w[to[i]];
  13. b[p]=son[p];
  14. }
  15. void Dfs(cri p,cri fa){
  16. for(int i=fir[p];i;i=l[i])if(to[i]!=fa)
  17. b[to[i]]+=b[p]-son[to[i]]-w[to[i]]+w[]-w[to[i]],Dfs(to[i],p);
  18. }
  19. void DFS(cri p,cri fa,cri be,cri dtt){
  20. dt[be%n+][p]=dtt;
  21. for(int i=fir[p];i;i=l[i])if(to[i]!=fa)DFS(to[i],p,be,dtt+);
  22. }
  23. void Gauss(){
  24. for(int i=;i<=n;++i){
  25. int best=i;double res;
  26. for(int j=i+;j<=n;++j)if(dt[j][i]>dt[best][i])best=j;
  27. for(int j=i;j<=n+;++j)res=dt[best][j],dt[best][j]=dt[i][j],dt[i][j]=res;
  28. for(int j=n+;j>=i;--j)dt[i][j]/=dt[i][i];
  29. for(int j=;j<=n;++j)if(i!=j)for(int k=n+;k>=i;--k)dt[j][k]-=dt[i][k]*dt[j][i];
  30. }
  31. }
  32. int main(){
  33. scanf("%d",&T);
  34. while(T--){
  35. scanf("%d",&n);
  36. for(int i=,aa,bb;i<n;++i)scanf("%d%d",&aa,&bb),connect(aa,bb);
  37. scanf("%d",&t);
  38. if(!t){
  39. for(int i=;i<=n;++i)scanf("%d",&a[i]);
  40. dfs(,);Dfs(,);
  41. for(int i=;i<=n;++i)printf("%d ",b[i]);puts("");
  42. }
  43. else{
  44. for(int i=,aa;i<=n;++i)scanf("%d",&aa),dt[i%n+][n+]=aa,DFS(i,,i,);
  45. Gauss();
  46. for(int i=;i<=n;++i)printf("%.0lf ",dt[i][n+]);puts("");
  47. }
  48. for(int p=;p<=n;++p)son[p]=fir[p]=w[p]=a[p]=b[p]=;cnt=;
  49. for(int i=;i<=;++i)for(int j=;j<=;++j)dt[i][j]=;
  50. }
  51. }

至此是考场上的垃圾40分代码(高斯消元炸了)

因为解唯一且都是正整数,所以其实可以不用实数域的高斯消元double卡精度,我们可以来一个整数域的。

原来你的高斯消元大概是这个样子的:

5x+y=8;2x+3y=11;

首先你选中了x绝对值最大的那个式子,把系数变为1。(有的板子没有这一步)

x+0.2y=1.6;2x+3y=11;

然后你又会拿第一个式子去消第二个的x。得到:

x+0.2y=1.6;2.6y=7.8;

y就出来了,是3,再回代得x是1。

多好的整数啊,但是你的高斯消元里面有很多浮点数运算,精度容易下降。

既然是整数,我们就用整数的方法做啊。想想你人工求解的过程:

还是这个式子:5x+y=8;2x+3y=11;

你会把它做类似于通分的操作,10x+2y=16;10x+15y=55;

虽然数字大了一点,但是是整数啊,只要没爆long long都没有问题。

做差,顺便还原前面那个式子:5x+y=8;13y=39;

呃啊,舒服。具体的代码实现也挺简单的,求个lcm就好了,用a*b/gcd(a,b)就行

暴力算法说多了。

测试点5,它连成了一条可爱的链。

我说过,

学长说过(为了更有说服力),数据范围不是白给的,有些具有提示作用。

那就肝它啊!肝出正解为止!

对于一条链我们现在知道它的b数组。求解a?

像刚才已知a数组求解b数组一样,我们设1是根而n是叶子。不断计算贡献是怎么叠加,去除的。

那么b数组的来源格外清晰明了:

设pre[i]为前i个节点的a值和,suc[i]为i~n的a值和。

b[i]=pre[1]+pre[2]+...+pre[i-2]+pre[i-1]+suc[i+1]+suc[i+2]+..+suc[n](原始式子)

再写一个

b[i-1]=pre[i]+pre[2]+...+pre[i-2]+suc[i]+suc[i+1]+suc[i+2]+...+suc[n]

数学上的什么错位相减。

b[i]-b[i-1]=pre[i-1]-suc[i](差值式子)

这个式子里面未知量不多,类似的我们可以列出一共n-1个这样的式子

可是里面的pre和suc都不一样不是很好求解。

设sum为所有点的a值和。根据定义的含义pre[i]+suc[i+1]=sum;->pre[i-1]=sum-suc[i]

那么上面的那个式子可以略微化简b[i]-b[i-1]=sum-2*suc[i];

类似的,我们还是有n-1个式子,它们现在有一个共同的未知量sum,这就好做一些了

只要知道sum,就能知道suc,就能知道a

但是,我们现在一共有n个未知量却只有n-1个式子,解不出来。

强行加一个!

我们一直运用的都是两个原始式子做差求得的差值式子,其实在这个过程中我们就不小心抛弃了某些有用的条件。

我们捡回原始式子,看哪个还能用?

b[1]=suc[2]+suc[3]+...+suc[n]

这个看起来比较漂亮。我们把我们的n-1个差值式子放在一起

b[n]-b[n-1]=sum-2*suc[n]

b[n-1]-b[n-2]=sum-2*suc[n-1]

b[n-2]-b[n-3]=sum-2*suc[n-2]

...

b[2]-b[1]=sum-2*suc[2]

左边这些东西一正一负的,把它们加起来貌似会消的挺干净

b[n]-b[1]=(n-1)*sum-2*(suc[n]+suc[n-1]+...+suc[3]+suc[2])

把那个能用的原始式子拿过来看一看:b[1]=suc[2]+suc[3]+...+suc[n]

后面的那一串suc是完全重复的!带进去!

b[n]-b[1]=(n-1)*sum-2*b[1]

sum=$ \frac{b[1]+b[n]}{n-1} $

漂亮啊!sum出来了,接下来每个差值式子就都只有一个未知量了

然后根据suc数组做差a数组就出来了,问题解决了!

于是我们折腾了这么半天,终于愉快的得到了...额外的10分

都说了这种测试数据是用来启发你的。

其实在树上这个式子也没有什么差别,只不过suc数组的含义变成了子树,即上述只是以1为根的特殊情况。

其实就和用a求b里面的那个son数组是一样的了

反正没人看,撇一个代码就溜啦。

  1. #include<cstdio>
  2. #define int long long
  3. #define cri const register int
  4. int T,t,n,a[],b[],cnt,SUM;
  5. int son[],fir[],l[],to[],w[];
  6. void connect(cri a,cri b){
  7. l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;
  8. l[++cnt]=fir[b];fir[b]=cnt;to[cnt]=a;
  9. }
  10. void dfs(cri p,cri fa){
  11. w[p]=a[p];
  12. for(int i=fir[p];i;i=l[i])if(to[i]!=fa)
  13. dfs(to[i],p),w[p]+=w[to[i]],son[p]+=son[to[i]]+w[to[i]];
  14. b[p]=son[p];
  15. }
  16. void Dfs(cri p,cri fa){
  17. for(int i=fir[p];i;i=l[i])if(to[i]!=fa)
  18. b[to[i]]+=b[p]-son[to[i]]-w[to[i]]+w[]-w[to[i]],Dfs(to[i],p);
  19. }
  20. void DFS(cri p,cri fa){
  21. if(p!=)SUM+=b[p]-b[fa];
  22. for(int i=fir[p];i;i=l[i])if(to[i]!=fa)DFS(to[i],p);
  23. }
  24. void dFs(cri p,cri fa){
  25. if(p!=)a[p]=son[p]=(SUM-b[p]+b[fa])>>;else a[p]=son[p]=SUM;
  26. for(int i=fir[p];i;i=l[i])if(to[i]!=fa)dFs(to[i],p),a[p]-=son[to[i]];
  27. }
  28. main(){
  29. scanf("%lld",&T);
  30. while(T--){
  31. scanf("%lld",&n);
  32. for(int i=,aa,bb;i<n;++i)scanf("%lld%lld",&aa,&bb),connect(aa,bb);
  33. scanf("%lld",&t);
  34. if(!t){
  35. for(int i=;i<=n;++i)scanf("%lld",&a[i]);
  36. dfs(,);Dfs(,);
  37. for(int i=;i<=n;++i)printf("%lld ",b[i]);puts("");
  38. }
  39. else{
  40. for(int i=;i<=n;++i)scanf("%lld",&b[i]);
  41. DFS(,);SUM+=b[]*;SUM/=(n-);dFs(,);
  42. for(int i=;i<=n;++i)printf("%lld ",a[i]);puts("");
  43. }
  44. for(int p=;p<=n;++p)son[p]=fir[p]=w[p]=a[p]=b[p]=;cnt=SUM=;
  45. }
  46. }

我恨高斯

单(single):换根dp,表达式分析,高斯消元的更多相关文章

  1. BZOJ3270: 博物馆【概率DP】【高斯消元】

    Description 有一天Petya和他的朋友Vasya在进行他们众多旅行中的一次旅行,他们决定去参观一座城堡博物馆.这座博物馆有着特别的样式.它包含由m条走廊连接的n间房间,并且满足可以从任何一 ...

  2. BZOJ3141 Hnoi2013 游走 【概率DP】【高斯消元】*

    BZOJ3141 Hnoi2013 Description 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点 ...

  3. CodeForces - 24D :Broken robot (DP+三对角矩阵高斯消元 随机)

    pro:给定N*M的矩阵,以及初始玩家位置. 规定玩家每次会等概率的向左走,向右走,向下走,原地不动,问走到最后一行的期望.保留4位小数. sol:可以列出方程,高斯消元即可,发现是三角矩阵,O(N* ...

  4. P4321-随机漫游【状压dp,数学期望,高斯消元】

    正题 题目链接:https://www.luogu.com.cn/problem/P4321 题目大意 给出\(n\)个点\(m\)条边的一张无向图,\(q\)次询问. 每次询问给出一个点集和一个起点 ...

  5. BZOJ 1299: [LLH邀请赛]巧克力棒 【SG函数/博弈分析/高斯消元】

    因为太懒,放个博客 我只写了O(2n)O(2^n)O(2n)的 CODE #include <cstdio> int n, x[15]; int main () { for(int T = ...

  6. 【概率DP/高斯消元】BZOJ 2337:[HNOI2011]XOR和路径

    2337: [HNOI2011]XOR和路径 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 682  Solved: 384[Submit][Stat ...

  7. 2016ACM/ICPC亚洲区沈阳站H - Guessing the Dice Roll HDU - 5955 ac自动机+概率dp+高斯消元

    http://acm.hdu.edu.cn/showproblem.php?pid=5955 题意:给你长度为l的n组数,每个数1-6,每次扔色子,问你每个串第一次被匹配的概率是多少 题解:先建成ac ...

  8. LA3490 Generator(KMP + 高斯消元)

    题意 一开始给你一个长为 \(S\) 的字符串. 从空串开始,不断在后面添加一个 \([A, A + n]\) 的一个字符. 第一次包含 \(S\) 的时候会停止添加.问期望的添加次数. 有 \(T\ ...

  9. 洛谷 P5643 - [PKUWC2018]随机游走(Min-Max 容斥+FWT+树上高斯消元,hot tea)

    题面传送门 一道挺综合的 hot tea,放到 PKUWC 的 D2T2 还挺喜闻乐见的( 首先我们考虑怎样对一个固定的集合 \(S\) 计算答案,注意到我们要求的是一个形如 \(E(\max(S)) ...

  10. 【BZOJ】1923 [Sdoi2010]外星千足虫(高斯消元)

    题目 传送门:QWQ 分析 高斯消元解异或方程组,和解普通方程组差不多. 范围有点大,要套一个bitset. 代码 #include <bits/stdc++.h> using names ...

随机推荐

  1. [LeetCode] 1137. N-th Tribonacci Number

    Description e Tribonacci sequence Tn is defined as follows: T0 = 0, T1 = 1, T2 = 1, and Tn+3 = Tn + ...

  2. 最简单的JS实现json转csv

    工作久了,总会遇到各种各样的数据处理工作,比如同步数据,初始化一些数据,目前比较流行的交互数据格式就是JSON,可是服务器中得到的JSON数据如果提供给业务人员看的话可能会非常不方便,这时候,转成CS ...

  3. 旧瓶新酒-获取网络资源即爬取下载页面内容(图片、html、css、js等)

    这个java获取网络资源以前也写过不少 最近用到又重新写了一个,apache.commons.io中的例子就非常好,但是无法对请求进行详细设置 于是大部分照搬,局部替换以设置请求头 如需更加复杂的设置 ...

  4. 02-05 scikit-learn库之线性回归

    目录 scikit-learn库之线性回归 一.LinearRegression 1.1 使用场景 1.2 代码 1.3 参数详解 1.4 属性 1.5 方法 1.5.1 报告决定系数 二.ARDRe ...

  5. js中的toString和valueOf

    数据的转换 基本上,所有JS数据类型都拥有valueOf和toString这两个方法,null除外.它们俩解决javascript值运算与显示的问题 所有对象继承了两个转换方法: 第一个是toStri ...

  6. POJ - 2393Yogurt factory

    The cows have purchased a yogurt factory that makes world-famous Yucky Yogurt. Over the next N (1 &l ...

  7. ubuntu下安装及配置git的方法

    安装Git 一个全新的ubunt系统,需要安装Git(系统是不具有该工具的),方法如下: 在terminel中输入如下命令: sudo apt-get install git git-core git ...

  8. django自关联,auth模块

    一.自关联 写蛮好的一篇博客:https://www.cnblogs.com/Kingfan1993/p/9936541.html 1.一对多关联 1.表内自关联是指表内数据相关联的对象和表是相同字段 ...

  9. php函数fsockopen的使用

    函数说明:fsockopen — 打开一个网络连接或者一个Unix套接字连接 语法: resource fsockopen ( string $hostname [, int $port = -1 [ ...

  10. liunx定时备份mongo数据库并实现自动删除N天前备份

    1.脚本文件: #!/bin/sh # dump 命令执行路径,根据mongodb安装路径而定 #!/bin/sh # dump 命令执行路径,根据mongodb安装路径而定 /bin/mongodu ...