HZOJ 单

Posted al-ca

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 
View Code

 

以上是关于HZOJ 单的主要内容,如果未能解决你的问题,请参考以下文章

[***]HZOJ 柱状图

[HZOJ10420]计算

HZOJ 太阳神

HZOJ 随

HZOJ 辣鸡(ljh)

HZOJ Permutation