HZOJ 20190727 T2 单(树上dp+乱搞?+乱推式子?+dfs?)

考试T2,考试时想到了40pts解法,即对于求b数组,随便瞎搞一下就oxxk,求a的话,很明显的高斯消元,但考试时不会打+没开double挂成10pts(我真sb),感觉考试策略还是不够成熟,而且感觉考试时间很不够用,一直在瞎yy+code,听讲题DeepinC 12min就打出了T150pts,这不仅是思维上的劣势,而且打代码的速度必须要加上来啊,不然就算有好想法也打不出来(也没啥好想法)。

接下来就是正经八本的题解了:

首先我们可以来一波玄学复杂度分析,数据范围1e5,要么$O(nlogn)$,要么$O(n)$,这是树上的问题,$O(nlogn)$的算法其实不多也就lca和乱七八糟的数据结构,但和这题显然不搭,所以我们尽量做到$O(n)$的复杂度。

首先来考虑给a求b,这是比较简单的一问,其实有点像树上dp,就是从父节点转移到子节点,首先我们可以$O(n)$求出不$b[1]$,然后就是开始考虑怎么转移,其实思考方式有点像「HAOI 2015」树上染色这题的思路都是考虑边对答案的影响,在回来看这题,如果从父节点转移到子节点那么,子节点子树外的点距离都加1,那么每个点的贡献都加了一个点权,但是子节点子树内的点却恰好相反,那么我们设$sum[i]$为以$i$为根的子树的点权和,那么用式子把我们刚才的分析表示出来就是$b[y]=b[x]+sum[1]-sum[y]-sum[y]=b[x]+sum[1]-2*sum[y]$,那么第一个问题就很好的在$O(n)$复杂度内得到了解决。

在来考虑给b求a,这是比较困难的一问,似乎除了高斯消元我们想不出更好的算法,那就只能硬着头皮推式子,这题好就好在转化很多,而且不能放过任何一个你已经推出来的式子,我们观察到上一问推出来的式子是和b数组有直接关系的,那么我们移项,得到$b[y]-b[x]=sum[1]-2*sum[y]$,这样就相当与把a,b数组建立了关系,其实这是很重要的思想,所有的题不都是给你已知量求未知量么?接着看题,我们设$dt[y]=b[y]-b[x]=sum[1]-2*sum[y]$,为什么要这么设呢,首先我们来证一个结论$b[1]=\Sigma_{i=2}^n{sum[i]}$,看上去很显然?蒟蒻博主并不这么觉得,我们来证一下不b[1]是什么,就是每个点的深度乘以每个点的点权,我们在来看右边的式子是所有sum[i]之和,那么每个点对右边式子的贡献就是点权乘上他有多少代祖宗,那这不就是深度吗,所以两边是相等的,证毕。

然后我们设$tot=\Sigma_{i=2}^n{dt[i]}=(n-1)sum[1]-2*\Sigma_{i=2}^n{sum[i]}=(n-1)sum[1]-2*b[1]$

这样我们就可以求出sum[1],然后求出整个sum[]数组,然后dfs求出a[]数组。

完结。

总结:我觉得这题特别吼啊,没有考很难的知识点,考的是对问题的转化和把一个难以解决的问题先分解成一个一个可以解决的小问题,再合并起来。  %%%liu_runda

  在来说一下自己,感觉就是考场上想的很不深入,好像就没有打算去肝正解,一直在很表层停留,这一定要改阿。

最后一点:临接表数组要开2背啊啊啊啊啊!!!

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<vector>
  5 #include<queue>
  6 #include<cstdio>
  7 #include<cmath>
  8 using namespace std;
  9 #define int long long
 10 const int N=1e5+10;
 11 int n,m;
 12 int first[N],nex[N],to[N],tot,total;
 13 int a[N],b[N],d[N],sum,vis[N],ansb[N],ansa[N],size[N],dt[N],v[N],vi[N],res[N];
 14 inline void add(int a,int b){ to[++tot]=b,nex[tot]=first[a],first[a]=tot;}
 15 void init(){
 16     tot=0;
 17     memset(first,0,sizeof(first));
 18     memset(to,0,sizeof(to));
 19     memset(nex,0,sizeof(nex));
 20     //memset(a,0,sizeof(a));
 21     //memset(b,0,sizeof(b));
 22     //memset(ansb,0,sizeof(ansb));
 23     //memset(ansa,0,sizeof(ansa));
 24     sum=0,total=0;
 25     memset(dt,0,sizeof(dt));
 26     memset(size,0,sizeof(size));
 27     //memset(vis,0,sizeof(vis));
 28     //memset(vi,0,sizeof(vi));
 29     //memset(v,0,sizeof(v));
 30     memset(res,0,sizeof(res));
 31     memset(d,0,sizeof(d));
 32     //memset(vic,0,sizeof(vic));
 33 }
 34 void dfs(int x,int fa){
 35     vis[x]=1;
 36     for(int i=first[x];i;i=nex[i]){
 37         int y=to[i];
 38         if(fa==y) continue;
 39         d[y]=d[x]+1;
 40         dfs(y,x);
 41         size[x]+=size[y];
 42     }
 43 }
 44 void dfs_1(int x,int fa){
 45     vis[x]=1;
 46     for(int i=first[x];i;i=nex[i]){
 47         int y=to[i];
 48         if(fa==y) continue;
 49         ansb[y]=ansb[x]+sum-2*size[y];
 50         dfs_1(y,x);
 51     }
 52 }
 53 void dfs_2(int x,int fa){
 54     v[x]=1;
 55     for(int i=first[x];i;i=nex[i]){
 56         int y=to[i];
 57         if(fa==y) continue;
 58         dt[y]=b[y]-b[x];
 59         dfs_2(y,x);
 60     }
 61 }
 62 void dfs_3(int x,int fa){
 63     vi[x]=1;
 64     for(int i=first[x];i;i=nex[i]){
 65         int y=to[i];
 66         if(fa==y) continue;
 67         ansa[x]-=res[y];
 68         dfs_3(y,x);
 69     }
 70 }
 71 signed main(){
 72     int T;
 73     scanf("%lld",&T);
 74     while(T--){
 75         init();
 76         scanf("%lld",&n);
 77         for(int i=1;i<n;i++){
 78             int x,y;
 79             scanf("%lld%lld",&x,&y);
 80             add(x,y);
 81             add(y,x);
 82         }
 83         int opt;
 84         scanf("%lld",&opt);
 85         if(opt==0){
 86             for(int i=1;i<=n;i++) {scanf("%lld",&a[i]);sum+=a[i];size[i]=a[i];}
 87             d[1]=1;
 88             dfs(1,0);
 89             for(int i=2;i<=n;i++) ansb[1]+=a[i]*(d[i]-1);
 90             memset(vis,0,sizeof(vis));
 91             dfs_1(1,0);
 92             for(int i=1;i<=n;i++) printf("%lld ",ansb[i]);
 93             puts("");
 94         }
 95         else{
 96             for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
 97             dfs_2(1,0);
 98             //for(int i=2;i<=n;i++) cout<<dt[i]<<" ";
 99             //cout<<endl;
100             total=0;
101             for(int i=2;i<=n;i++) total+=dt[i];
102             //cout<<total<<endl;
103             res[1]=(2*b[1]+total)/(n-1);
104             for(int i=2;i<=n;i++) res[i]=(res[1]-dt[i])/2;
105             for(int i=1;i<=n;i++) ansa[i]=res[i];
106             dfs_3(1,0);
107             for(int i=1;i<=n;i++) printf("%lld ",ansa[i]);
108             puts("");
109         }
110     }
111 }

原文地址:https://www.cnblogs.com/leom10/p/11258121.html

时间: 2024-11-08 12:49:37

HZOJ 20190727 T2 单(树上dp+乱搞?+乱推式子?+dfs?)的相关文章

学渣乱搞系列之dp斜率优化

学渣乱搞系列之dp斜率优化 By 狂徒归来 貌似dp的斜率优化一直很难搞啊,尤其是像我这种数学很挫的学渣,压根不懂什么凸包,什么上凸下凸的,哎...说多了都是泪,跟wdd讨论了下,得出一些结论.本文很大部分参考了大神Accept的文章,不过此神貌似早已绝迹江湖,这篇文章写得好,也写得很差,前半部分叙述得很好,可是关键,关键部分说得很乱,有些许错误,很多大神都进行了评论指出,但是大神Accept貌似没有修改的意思,故重新总结下,以便自己以后查阅和复习啊. 下面看一个例题Print Article.

Codeforces 1077E (二分乱搞或者dp)

题意:给你一个数组,可以从中选区若干种元素,但每种元素选区的个数前一种必须是后一种的2倍,选区的任意2种元素不能相同,问可以选取最多的元素个数是多少? 思路1(乱搞):记录一下每种元素的个数,然后暴力枚举最少的元素个数,计算符合题意的最优情况. 代码: #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<map> #include<s

【BZOJ-4692】Beautiful Spacing 二分答案 + 乱搞(DP?)

4692: Beautiful Spacing Time Limit: 15 Sec  Memory Limit: 128 MBSubmit: 46  Solved: 21[Submit][Status][Discuss] Description 文章是一些单词组成的序列,单词由字母组成.你的任务是将一篇文章的单词填充到一个网格中,其中网格包含W列和足够多的行.为了布局之美,以下限制都需要满足. 1.文章中的文字需要按照原有的顺序放置.下图表示了将4个单词的文章“This is a pen”放入

codeforces 653C C. Bear and Up-Down(乱搞题)

题目链接: C. Bear and Up-Down time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output The life goes up and down, just like nice sequences. Sequence t1, t2, ..., tn is called nice if the following two

CodeForces 690C2 Brain Network (medium)(树上DP)

题意:给定一棵树中,让你计算它的直径,也就是两点间的最大距离. 析:就是一个树上DP,用两次BFS或都一次DFS就可以搞定.但两次的时间是一样的. 代码如下: #include<bits/stdc++.h> using namespace std; const int maxn = 1e5 + 5; vector<int> G[maxn]; int f[maxn], g[maxn], l[maxn]; int dfs(int root, int fa){ if(f[root] !=

UVA 11853 [dfs乱搞]

/* 大连热身E题 不要低头,不要放弃,不要气馁,不要慌张 题意: 在1000×1000的格子内有很多个炮弹中心,半径给定. 为某人能否从西部边界出发,从东部边界走出. 不能输出不能,能的话输出最北边的入口和出口的坐标. 思路: dfs乱搞题.把炮弹辐射范围连在一起的炮弹看作一个整体,记录下它围起来的边界区域. 然后找到最北边的输出. */ #include<bits/stdc++.h> using namespace std; double x[1005],y[1005],r[1005];

Codeforces 732e [贪心][stl乱搞]

/* 不要低头,不要放弃,不要气馁,不要慌张 题意: 给n个插座,m个电脑.每个插座都有一个电压,每个电脑都有需求电压. 每个插座可以接若干变压器,每个变压器可以使得电压变为x/2上取整. 有无限个变压器供应. 问最多能使得多少个插座与电脑匹配,使得电压一致. 如果有多种方案,输出需要变压器总数最小的那种. 输出匹配数量 输出每个插座需要接多少个变压器.输出每台电脑匹配哪个插座. 思路: 贪心 乱搞 先从小到大将插座排序,然后从地第一个插座开始,不断除以2上取整.不断找是否可以匹配.找到匹配就停

bzoj 2286 [Sdoi2011]消耗战(虚树+树上DP)

2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1276  Solved: 445[Submit][Status][Discuss] Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸

Bucharest, Romania 2013 H Red John Game 乱搞 未解

题意:给你一个无限的棋盘,里面有n×n的棋子,每个棋子可以跳过一个相邻(4个方向)棋子跳到下一个位置,并把他的相邻位(他跳过的那一位)删除. 解题思路:判断n%3 是否等于 0 , 原因未知. 解题代码: 1 #include<stdio.h> 2 int main(){ 3 int n ; 4 while(~scanf("%d",&n)) 5 { 6 if(n%3) 7 printf("1\n"); 8 else printf("0\