HZOJ 单
两个子任务真的是坑……考试的时候想到了60分的算法,然而只拿到了20分(各种沙雕错,没救了……)。
算法1:
对于测试点1,直接n遍dfs即可求出答案,复杂度O(n^2),然而还是有好多同学跑LCA/最短路……
期望得分10;
算法2(搬运题解,因为这个我没有想到……):
t=1的数据最直接的想法是枚举所有可能的a[]数组判断是否可行.第2个测试点n<=5,1<=a[i]<=20.注意20^5=3200000,直接暴力搜索a[i]的取值是可以承受的,可以通过第2个测试点,期望得分10分,结合算法1,期望得分20分.
算法3:
对于测试点6,7。我们可以发现每个点的dis都有联系,这不就是二次扫描与换根吗(具体看代码理解)。结合算法2期望得分40;
1 void dfs2(int x,int fa) 2 { 3 for(int i=f(x);i;i=n(i)) 4 if(v(i)!=fa) 5 { 6 b[v(i)]=b[x]; 7 b[v(i)]-=sum[v(i)]; 8 b[v(i)]+=sum[x]-sum[v(i)]; 9 sum[x]-=sum[v(i)]; 10 sum[v(i)]+=sum[x]; 11 dfs2(v(i),x); 12 sum[v(i)]-=sum[x]; 13 sum[x]+=sum[v(i)]; 14 } 15 }
算法4:
t=1的数据中,b数组的表达式写出来之后,每个b[i]对应一个关于a[i]和树上两点距离的方程,例如b[1]=dis(1,1)*a[1]+dis(1,2)*a[2]+dis(1,3)*a[3]+...+dis(1,n)*a[n],于是可以列出n个方程用高斯消元求解,可以通过第2,3,4个测试点,期望得分30分,结合算法3,期望得分60分。要注意的一点是,高斯消元中要注意这句话‘if(fabs(sa[j][i])>fabs(sa[i][i]))
’,本人死于此……还有不少人死于强制类型转化的四舍五入,其实printf("%0.0lf")即可;
之后的算法题解写的已经很清晰了,我觉得我也写不了这么清楚,就直接搬运了。
算法5:
对于测试点5,树退化为1条链。这个测试点的作用主要在于启发选手想到正解的思路(然而我并没有什么启发……)。
t=0时,我们分别考虑编号小于i和大于i的点对b[i]的贡献.那么离得越远的点对答案的贡献越大.分别考虑每条边对答案的贡献,那么左侧的某条边对答案的贡献就是这条边左侧全部a[i]之和.实际上,我们对a[i]分别求取两次前缀和,两次后缀和即可完成对b[i]的计算.
记suf(i)为a[i]+a[i+1]+....+a[n-1]+a[n],pre(i)为a[1]+a[2]+...+a[i-1]+a[i],则b[i]=pre(1)+pre(2)+...+pre(i-1)+suf(i+1)+suf(i+2)+...+suf(n-1)+suf(n)
考虑t=1的情况.
我们知道suf(2)+suf(3)+...+suf(n)的值(即b[1]),知道pre(1) + suf(3) + suf(4) +...+suf(n)的值(即b[2]),知道pre(1)+pre(2)+suf(4)+...+suf(n)的值(即b[3]),注意到这些式子有很多项是一样的,考虑作差.可以得到下面的式子:
b[2]-b[1]=pre(1)-suf(2),b[3]-b[2]=pre(2)-suf(3).....b[i+1]-b[i]=pre(i)-suf(i+1)
这些式子是有实际意义的,考虑从b[1]变到b[2]时答案的变化量,变化的就是1和2之间连边的贡献.
同时,记SUM=a[1]+a[2]+...+a[n-1]+a[n],显然pre(i)+suf(i+1)=SUM,因此pre(i)=SUM-suf(i+1),将上面式子中所有pre换成suf,我们就知道了
SUM-2*suf(2) ,SUM-2*suf(3)...SUM-2*suf(n)的取值。
注意我们将n个式子作差之后得到了n-1个等式,实际上是丢弃了一些信息,只保留了b[i]之间的相对大小而忽略了b[i]自身的数值大小.于是考虑b[1]=suf(2)+suf(3)+suf(4)+... +suf(n),我们发现suf(2)到suf(n)都恰好出现了一次,而之前得到了n-1个形如SUM-2*suf(i)的式子,将这n-1个式子相加,suf(2),suf(3)...suf(n)前面的系数都是2,SUM的系数为(n-1),那么把这个式子加上2*b[1],就得到了(n-1)*SUM,将求得的SUM代入之前的n-1个式子可以得到suf(2),suf(3),suf(4)......suf(n),差分一下即可得到a数组.
时间复杂度O(n),可以通过第5个测试点.推出这个做法,树上的做法也就不难想了.
算法6(满分算法):
考虑树上的问题.
t=0的时候,我们可以先暴力计算出b[1],然后从b[1]出发开始dfs,由某个点的父亲的b[i]推出这个点的b[i],变化的是这个点和父亲的连边的贡献,也就是这条边两侧的点的a[i]之和的差值.
t=1的时候,顺着链上的思路,我们先随便找一个点为根建树,将有边直接相连的两个点的b[i]作差.设x的父亲为prt[x],以x为根的子树中所有a[i]之和为sum(x), SUM=a[1]+a[2]+...+a[n-1]+a[n],那么b[x]-b[prt[x]]=(SUM-sum(x))-sum(x).
同链的情况一样,得到n-1个式子,将树根的b[i]也列出式子,可以求出全部a[i]之和,然后就可以根据之前的式子推出所有的a[i],和链的做法类似,不再赘述.
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #define MAXN 100010 6 #define int LL 7 #define LL long long 8 #define esp 1e-8 9 #define ma(x) memset(x,0,sizeof(x)) 10 using namespace std; 11 struct edge 12 { 13 int u,v,nxt; 14 #define u(x) ed[x].u 15 #define v(x) ed[x].v 16 #define n(x) ed[x].nxt 17 }ed[MAXN*2]; 18 int first[MAXN],num_e; 19 #define f(x) first[x] 20 int T,n,t; 21 LL a[MAXN],b[MAXN]; 22 LL dis[MAXN],sum[MAXN]; 23 LL SUM,suf[MAXN]; 24 LL SUM_2suf[MAXN]; 25 LL SUM_2sum[MAXN]; 26 LL TSUM=0; 27 28 int map[110][110]; 29 double sa[110][110],sb[110]; 30 void GS() 31 { 32 for(int i=1;i<=n;i++) 33 { 34 for(int j=i;j<=n;j++) 35 if(fabs(sa[j][i])>fabs(sa[i][i])) 36 { 37 for(int k=1;k<=n;k++) 38 swap(sa[i][k],sa[j][k]); 39 swap(sb[i],sb[j]); 40 } 41 for(int j=1;j<=n;j++) 42 { 43 if(j==i)continue; 44 if(fabs(sa[i][i])<=esp)continue; 45 double cal=sa[j][i]/sa[i][i]; 46 for(int k=1;k<=n;k++) 47 sa[j][k]-=sa[i][k]*cal; 48 sb[j]-=sb[i]*cal; 49 } 50 } 51 } 52 void dfs(int x,int fa) 53 { 54 sum[x]=a[x]; 55 for(int i=f(x);i;i=n(i)) 56 if(v(i)!=fa) 57 { 58 dis[v(i)]=dis[x]+1; 59 dfs(v(i),x); 60 sum[x]+=sum[v(i)]; 61 } 62 } 63 void dfs2(int x,int fa) 64 { 65 for(int i=f(x);i;i=n(i)) 66 if(v(i)!=fa) 67 { 68 b[v(i)]=b[x]; 69 b[v(i)]-=sum[v(i)]; 70 b[v(i)]+=sum[x]-sum[v(i)]; 71 sum[x]-=sum[v(i)]; 72 sum[v(i)]+=sum[x]; 73 dfs2(v(i),x); 74 sum[v(i)]-=sum[x]; 75 sum[x]+=sum[v(i)]; 76 } 77 } 78 void dfs3(int x,int fa) 79 { 80 if(x!=1)SUM_2sum[x]=b[x]-b[fa]; 81 for(int i=f(x);i;i=n(i)) 82 if(v(i)!=fa) 83 dfs3(v(i),x); 84 } 85 void dfs4(int x,int fa) 86 { 87 if(x==1)sum[x]=SUM; 88 else sum[x]=(SUM-SUM_2sum[x])/2; 89 a[x]=sum[x]; 90 for(int i=f(x);i;i=n(i)) 91 if(v(i)!=fa) 92 { 93 dfs4(v(i),x); 94 a[x]-=sum[v(i)]; 95 } 96 } 97 inline int read(); 98 inline void add(int u,int v); 99 signed main() 100 { 101 // freopen("in.txt","r",stdin); 102 103 T=read(); 104 while(T--) 105 { 106 ma(sum);ma(first);num_e=0;ma(dis);ma(a);ma(b); 107 ma(map);ma(sa);ma(sb);ma(suf);SUM=0;ma(SUM_2suf); 108 ma(SUM_2sum);TSUM=0; 109 bool pd=1; 110 n=read();int u,v; 111 for(int i=1;i<n;i++) 112 { 113 u=read(),v=read(); 114 if(v!=u+1)pd=0; 115 add(u,v),add(v,u); 116 } 117 t=read(); 118 if(t==0) 119 { 120 for(int i=1;i<=n;i++)a[i]=read(); 121 if(n<=1000) 122 { 123 for(int i=1;i<=n;i++) 124 { 125 dis[i]=0; 126 dfs(i,0); 127 for(int j=1;j<=n;j++)b[i]+=a[j]*dis[j]; 128 } 129 for(int i=1;i<=n;i++) 130 printf("%lld ",b[i]); 131 puts(""); 132 continue; 133 } 134 else 135 { 136 dis[1]=0;dfs(1,0); 137 for(int i=1;i<=n;i++)b[1]+=dis[i]*a[i]; 138 dfs2(1,0); 139 for(int i=1;i<=n;i++) 140 printf("%lld ",b[i]); 141 puts(""); 142 continue; 143 } 144 } 145 if(t==1) 146 { 147 for(int i=1;i<=n;i++)b[i]=read(); 148 if(n<=100) 149 { 150 for(int i=1;i<=n;i++) 151 { 152 dis[i]=0;dfs(i,0); 153 for(int j=1;j<=n;j++)map[i][j]=dis[j]; 154 } 155 for(int i=1;i<=n;i++)sb[i]=b[i]; 156 for(int i=1;i<=n;i++) 157 for(int j=1;j<=n;j++)sa[i][j]=map[j][i]; 158 GS(); 159 for(int i=1;i<=n;i++) 160 printf("%0.0lf ",sb[i]/sa[i][i]); 161 puts(""); 162 continue; 163 } 164 if(pd) 165 { 166 for(int i=2;i<=n;i++)SUM_2suf[i]=b[i]-b[i-1]; 167 LL tem=0; 168 for(int i=2;i<=n;i++)tem+=SUM_2suf[i]; 169 tem+=b[1]*2; 170 SUM=tem/(n-1); 171 for(int i=2;i<=n;i++)suf[i]=(SUM-SUM_2suf[i])/2; 172 suf[1]=SUM;suf[n+1]=0; 173 for(int i=1;i<=n;i++)a[i]=suf[i]-suf[i+1]; 174 for(int i=1;i<=n;i++) 175 printf("%lld ",a[i]); 176 puts(""); 177 continue; 178 } 179 else 180 { 181 dfs3(1,0); 182 for(int i=2;i<=n;i++)TSUM+=SUM_2sum[i]; 183 TSUM+=b[1]*2; 184 SUM=TSUM/(n-1); 185 dfs4(1,0); 186 for(int i=1;i<=n;i++) 187 printf("%lld ",a[i]); 188 puts(""); 189 continue; 190 } 191 } 192 } 193 } 194 inline int read() 195 { 196 int s=0;char a=getchar(); 197 while(a<'0'||a>'9')a=getchar(); 198 while(a>='0'&&a<='9'){s=s*10+a-'0';a=getchar();} 199 return s; 200 } 201 inline void add(int u,int v) 202 { 203 ++num_e; 204 u(num_e)=u; 205 v(num_e)=v; 206 n(num_e)=f(u); 207 f(u)=num_e; 208 }