P1351 联合权值 noip提高组2014

xiaoxiao2021-02-28  75

题目描述

无向连通图G 有n 个点,n - 1 条边。点从1 到n 依次编号,编号为 i 的点的权值为W i ,每条边的长度均为1 。图上两点( u , v ) 的距离定义为u 点到v 点的最短距离。对于图G 上的点对( u, v) ,若它们的距离为2 ,则它们之间会产生Wu×Wv 的联合权值。

请问图G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少?

输入输出格式

输入格式:

输入文件名为link .in。

第一行包含1 个整数n 。

接下来n - 1 行,每行包含 2 个用空格隔开的正整数u 、v ,表示编号为 u 和编号为v 的点之间有边相连。

最后1 行,包含 n 个正整数,每两个正整数之间用一个空格隔开,其中第 i 个整数表示图G 上编号为i 的点的权值为W i 。

输出格式:

输出文件名为link .out 。

输出共1 行,包含2 个整数,之间用一个空格隔开,依次为图G 上联合权值的最大值

和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对10007 取余。

输入输出样例

输入样例#1: 5 1 2 2 3 3 4 4 5 1 5 2 3 10 输出样例#1: 20 74

说明

本例输入的图如上所示,距离为2 的有序点对有( 1,3) 、( 2,4) 、( 3,1) 、( 3,5) 、( 4,2) 、( 5,3) 。

其联合权值分别为2 、15、2 、20、15、20。其中最大的是20,总和为74。

【数据说明】

对于30% 的数据,1 < n≤ 100 ;

对于60% 的数据,1 < n≤ 2000;

对于100%的数据,1 < n≤ 200 , 000 ,0 < wi≤ 10, 000 。

这题是去年时候一直做不过去的,后来知道用并查集的方法可以过六十分。今年再看,突然变水了,看数据范围只能用O(n)或者O(n*log(n))的方法过,于是我们可以考虑在这个复杂度范围内的算法,但是这时,根据题意可以发现,只有n-1条边,意味着这是一棵树,于是我们再考虑答案的统计,以节点h为子树的总答案等于h所有子节点总的答案加上h所有孙子节点的和与h权值的乘积(乘法分配率)再加上h的子节点的权值与其他h的子节点的权值的乘积。这样,只要维护几个数组即可,sum数组统计总答案,maxx数组,记录以h为根的儿子结点的最大权值,son记录儿子结点的权值和,于是就A掉了

//此题以前做的时候操之过急,没有考虑周全,现在重新整理一下思路,thinktwice, codeonce //首先想好要维护哪些数组,第一,sum数组,统计答案(也就是总的联合权值),son数组,记录某个节点的儿子节点的权值之和, //每次统计时用w[h]*son[v],v是h的儿子节点,但这里还有一种情况,就是两个儿子结点之间的联合权值,观察可以得到 //每搜到一个子节点,就用维护的maxx数组维护已搜到的子节点,用当前子节点的权值与其相乘更新最大值,并用当前子节点的权值 //乘上已搜到的子节点的权值和即为联合权值总数,唯一要担心的恐怕就是递归报站吧 #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define For(i,a,b) for(i=(a);i<=(b);++i) #define rep(i,a,b) for(i=(a);i>=(b);--i) #define ll long long #define mm(a,b) memset(a,b,sizeof(a)) #define inf 999999999 #define mod 10007 using namespace std; int read(){ int sum = 0,fg = 1; char c = getchar(); while(c < '0' || c > '9'){if(c == '-')fg = -1;c = getchar();} while(c >='0' && c <='9')sum = sum*10 + c-'0',c = getchar(); return sum * fg; } ll readll(){ ll sum = 0,fg = 1; char c = getchar(); while(c < '0' || c > '9'){if(c == '-')fg = -1;c = getchar();} while(c >='0' && c <='9')sum = sum*10 + c-'0',c = getchar(); return sum * fg; } const int maxn = 400010; const int maxm = 200010; int n; int Begin[maxn],to[maxn],Next[maxn],e; void add(int x,int y){ to[++e] = y; Next[e] = Begin[x]; Begin[x] = e; } int vis[maxm]; ll ans_max ,w[maxm] ,maxx[maxm] ,son[maxm] ,sum[maxm]; void dfs(int h){ vis[h] = 1; int v; for(int i = Begin[h] ;i; i = Next[i]){ if(!vis[v = to[i]]){ dfs(v); sum[h] = (sum[h] + w[v] * son[h])%mod; son[h] += w[v]; ans_max = max(ans_max , maxx[h]*w[v]); maxx[h] = max(maxx[h] , w[v]); ans_max = max(ans_max , maxx[v]*w[h]); sum[h] = (sum[h] + sum[v])%mod; sum[h] = (sum[h] + w[h] * son[v])%mod; } } } int main(){ int i,j; n = read(); For(i,1,n-1){ int x = read(),y = read(); add(x,y);add(y,x); } For(i,1,n){ w[i] = readll(); } dfs(1); printf("%lld %lld\n",ans_max,sum[1]%mod*2%mod); return 0; }

转载请注明原文地址: https://www.6miu.com/read-76082.html

最新回复(0)