NOIP2014Day1T2 联合权值
题目分析:
题目的第一句话告诉你这个图是一个树。
我们可以想到,对于节点u,每个与它相连的节点v,v的集合两两不重复的w的乘积就是题目所求。注意到题目要求为有序点对,也就是说(u,v)和(v,u)要分别计入答案。但是我们只要计算其中的一个,最后将答案乘2即可。
首先可以想到:枚举树上的所有点u,枚举u的所有边e1,枚举e1之后的每一个边e2,e1和e2的入点的w之积为一个答案。将它们累加如SUM,并与MAX比较。最后SUM=SUM*2 。
优化:
当我们枚举u的所有出边,假设这些边的入点为v1,v2,v3,v4,则以u为中继点的联合权值之和为:
S=v1v2+v1v3+v1v4+v2v3+v2v4+v3v4
利用分配率合并:
S=v2v1+v3(v1+v2)+v4(v1+v2+v3)
因此,枚举u的所有出边e1时,设s代表已经枚举的e1的入点的w的和,就可以省去枚举e2的步骤。
如是优化,时间复杂度为O(N+M)
代码细节:
也没什么细节。请参考代码部分。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 7 //variable// 8 int last[200100],prel[400100],dest[400100],tot=1; 9 int n,w[200100],maxx=-1e9,ans=0,mod=10007; 10 11 //function prototype// 12 void addline(int,int); 13 void dfs(int,int); 14 15 //solve//noip2014 day1 t2 16 int main(){ 17 scanf("%d",&n); 18 int u,v; 19 for (int i=1;i<n;++i){ 20 scanf("%d%d",&u,&v); 21 addline(u,v); 22 addline(v,u); 23 } 24 for (int i=1;i<=n;++i){ 25 scanf("%d",w+i); 26 } 27 dfs(1,0); 28 printf("%d %d\n",maxx,(ans*2)%mod); 29 } 30 31 void addline(int u,int v){ 32 dest[++tot]=v; 33 prel[tot]=last[u]; 34 last[u]=tot; 35 } 36 37 void dfs(int x,int fa){ 38 int sum=w[fa],MAX=w[fa]; 39 for (int k=last[x];k;k=prel[k]){ 40 if (dest[k]==fa) continue; 41 dfs(dest[k],x); 42 ans=(ans+sum*w[dest[k]])%mod; 43 sum=(sum+w[dest[k]])%mod; 44 maxx=max(maxx,MAX*w[dest[k]]); 45 MAX=max(MAX,w[dest[k]]); 46 } 47 }