HZOJ 20190727 T2 单(树上dp+乱搞?+乱推式子?+dfs?)
Posted leom10
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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^nsum[i]$,看上去很显然?蒟蒻博主并不这么觉得,我们来证一下不b[1]是什么,就是每个点的深度乘以每个点的点权,我们在来看右边的式子是所有sum[i]之和,那么每个点对右边式子的贡献就是点权乘上他有多少代祖宗,那这不就是深度吗,所以两边是相等的,证毕。
然后我们设$tot=\\Sigma_i=2^ndt[i]=(n-1)sum[1]-2*\\Sigma_i=2^nsum[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
以上是关于HZOJ 20190727 T2 单(树上dp+乱搞?+乱推式子?+dfs?)的主要内容,如果未能解决你的问题,请参考以下文章