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

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

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

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

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

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

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

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

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

 void dfs(cri p,cri fa){
w[p]=a[p];
for(int i=fir[p];i;i=l[i])if(to[i]!=fa)
dfs(to[i],p),w[p]+=w[to[i]],son[p]+=son[to[i]]+w[to[i]];
b[p]=son[p];
}

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

根节点的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)

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

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

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

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

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

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

 #include<cstdio>
#define cri const register int
int T,t,n,a[],b[],cnt;double dt[][];
int son[],fir[],l[],to[],w[];
void connect(cri a,cri b){
l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;
l[++cnt]=fir[b];fir[b]=cnt;to[cnt]=a;
}
void dfs(cri p,cri fa){
w[p]=a[p];
for(int i=fir[p];i;i=l[i])if(to[i]!=fa)
dfs(to[i],p),w[p]+=w[to[i]],son[p]+=son[to[i]]+w[to[i]];
b[p]=son[p];
}
void Dfs(cri p,cri fa){
for(int i=fir[p];i;i=l[i])if(to[i]!=fa)
b[to[i]]+=b[p]-son[to[i]]-w[to[i]]+w[]-w[to[i]],Dfs(to[i],p);
}
void DFS(cri p,cri fa,cri be,cri dtt){
dt[be%n+][p]=dtt;
for(int i=fir[p];i;i=l[i])if(to[i]!=fa)DFS(to[i],p,be,dtt+);
}
void Gauss(){
for(int i=;i<=n;++i){
int best=i;double res;
for(int j=i+;j<=n;++j)if(dt[j][i]>dt[best][i])best=j;
for(int j=i;j<=n+;++j)res=dt[best][j],dt[best][j]=dt[i][j],dt[i][j]=res;
for(int j=n+;j>=i;--j)dt[i][j]/=dt[i][i];
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];
}
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=,aa,bb;i<n;++i)scanf("%d%d",&aa,&bb),connect(aa,bb);
scanf("%d",&t);
if(!t){
for(int i=;i<=n;++i)scanf("%d",&a[i]);
dfs(,);Dfs(,);
for(int i=;i<=n;++i)printf("%d ",b[i]);puts("");
}
else{
for(int i=,aa;i<=n;++i)scanf("%d",&aa),dt[i%n+][n+]=aa,DFS(i,,i,);
Gauss();
for(int i=;i<=n;++i)printf("%.0lf ",dt[i][n+]);puts("");
}
for(int p=;p<=n;++p)son[p]=fir[p]=w[p]=a[p]=b[p]=;cnt=;
for(int i=;i<=;++i)for(int j=;j<=;++j)dt[i][j]=;
}
}

至此是考场上的垃圾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数组是一样的了

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

 #include<cstdio>
#define int long long
#define cri const register int
int T,t,n,a[],b[],cnt,SUM;
int son[],fir[],l[],to[],w[];
void connect(cri a,cri b){
l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;
l[++cnt]=fir[b];fir[b]=cnt;to[cnt]=a;
}
void dfs(cri p,cri fa){
w[p]=a[p];
for(int i=fir[p];i;i=l[i])if(to[i]!=fa)
dfs(to[i],p),w[p]+=w[to[i]],son[p]+=son[to[i]]+w[to[i]];
b[p]=son[p];
}
void Dfs(cri p,cri fa){
for(int i=fir[p];i;i=l[i])if(to[i]!=fa)
b[to[i]]+=b[p]-son[to[i]]-w[to[i]]+w[]-w[to[i]],Dfs(to[i],p);
}
void DFS(cri p,cri fa){
if(p!=)SUM+=b[p]-b[fa];
for(int i=fir[p];i;i=l[i])if(to[i]!=fa)DFS(to[i],p);
}
void dFs(cri p,cri fa){
if(p!=)a[p]=son[p]=(SUM-b[p]+b[fa])>>;else a[p]=son[p]=SUM;
for(int i=fir[p];i;i=l[i])if(to[i]!=fa)dFs(to[i],p),a[p]-=son[to[i]];
}
main(){
scanf("%lld",&T);
while(T--){
scanf("%lld",&n);
for(int i=,aa,bb;i<n;++i)scanf("%lld%lld",&aa,&bb),connect(aa,bb);
scanf("%lld",&t);
if(!t){
for(int i=;i<=n;++i)scanf("%lld",&a[i]);
dfs(,);Dfs(,);
for(int i=;i<=n;++i)printf("%lld ",b[i]);puts("");
}
else{
for(int i=;i<=n;++i)scanf("%lld",&b[i]);
DFS(,);SUM+=b[]*;SUM/=(n-);dFs(,);
for(int i=;i<=n;++i)printf("%lld ",a[i]);puts("");
}
for(int p=;p<=n;++p)son[p]=fir[p]=w[p]=a[p]=b[p]=;cnt=SUM=;
}
}

我恨高斯

单(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. scalikejdbc 学习笔记(5)

    常用增删改查操作: import scalikejdbc._ import scalikejdbc.config._ object CommonOperation { def main(args: A ...

  2. 关于SpringBoot 1.x和2.x版本差别

    有点小差别 基本上基于SpringBoot的代码不需要改动,但有些配置属性和配置类,可能要改动,改动原因是 配置和类的更新或者是改名一般正常的MVC,数据库访问这些都不需要改动,下面按照本书章节说明区 ...

  3. oracle表空间不足:ORA-01653: unable to extend table

    问题背景: oracle表空间不足报错是比较常见的故障,尤其是没有对剩余表空间做定期巡检的系统: 报错代码如下: oracle表空间不足错误代码:ORA-01653: unable to extend ...

  4. HashTable、Dictionary、ConcurrentDictionary三者区别

    转载自https://blog.csdn.net/yinghuolsx/article/details/72952857 1.HashTable HashTable表示键/值对的集合.在.NET Fr ...

  5. MacOS 导入MySQLdb 报错解决思路(解决ImportError: this is MySQLdb version (1, 2, 3, 'beta', 1), but _mysql is version (1, 2, 5, 'final', 1))

    cd /Library/Python/2.7/site-packages ls rm -rf MySQL_python-1.2.5-py2.7.egg-info 然后重新import 即可

  6. SQLMAP SSI注入错误解决

    记一次SQL注入 目标地址:https://www.xxxx.com/ 之前补天提交过这个注入  后来貌似”修复了”(实际就是装了安全狗和过滤了一些关键字) 不过今天试了下 还是可以注入 可以看到已经 ...

  7. TensorFlow2.0(9):TensorBoard可视化

    .caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px so ...

  8. PHP代码审计基础-初级篇

    对于php代码审计我也是从0开始学的,对学习过程进行整理输出沉淀如有不足欢迎提出共勉.对学习能力有较高要求,整个系列主要是在工作中快速精通php代码审计,整个学习周期5天 ,建议花一天时间熟悉php语 ...

  9. 关于Linux中的 localhost 默认地址简单介绍

    大家都知道localhost指的是本机的IP地址:127.0.0.1 用于回路测试,那能不能修改localhost呢,答案肯定是可以的 打开终端--->输入: vim /etc/host  然后 ...

  10. Arduino学习笔记⑦ EEPROM断电保存数据

    1.前言     EEPROM,叫做电可擦可编程可读寄存器(是不是觉得好官方,不知道是什么鬼?反正我也一脸懵逼),只需要知道这是一种断电后数据不会丢失的存储设备,可以用来应对需要做记录做保存的场合.简 ...