【问题描述】 | ||
无向连通图 G 有 n 个点,n-1 条边。点从 1 到 n 依次编号,编号为 i 的点的权值为 W_i, 每条边的长度均为 1。图上两点(u, v)的距离定义为 u 点到 v 点的最短距离。对于图 G 上的点对(u, v),若它们的距离为 2,则它们之间会产生(W_u)×(W_v)的联合权值。 请问图 G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少? |
【输入格式】 | |||
第一行包含1个整数 n。 接下来n-1行,每行包含2个用空格隔开的正整数u、v,表示编号为u和编号为v的点之间有边相连。 最后1行,包含n个正整数,每两个正整数之间用一个空格隔开,其中第i 个整数表示 图G上编号为i的点的权值为Wi 。 |
【输出格式】 | |||
输出共 1 行,包含 2 个整数,之间用一个空格隔开,依次为图 G 上联合权值的最大值 和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对10007取余。 |
【输入样例】 | |||
5 1 2 2 3 3 4 4 5 1 5 2 3 10 |
【输出样例】 | |||
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。 |
【题解】
70分方法:
dfs枚举节点1...n,计算以枚举节点为根的深度为2的节点,还可以加一些剪枝
这种方法编程难度不大,但时间复杂度可以达到O(n^2),最多60分,我得了70分
代码(不能满分):
#include<iostream>
#include<cstdio>
#include<cctype>
const int maxn=200005;
const int mo=10007;
using namespace std;
struct edge{
int to,next;
}e[maxn<<1];
int first[maxn],np=0,w[maxn],n;
int now_p,max_w=0,sum_w=0;
void scan(int& in){
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
in=0;
while(isdigit(ch)) in=in*10+ch-'0',ch=getchar();
}
void addedge(int u,int v){
e[++np]=(edge){v,first[u]};
first[u]=np;
}
void init(){
int x,y;
scanf("%d",&n);
for(int i=1;i<n;i++){
scan(x),scan(y);
addedge(x,y);
addedge(y,x);
}
for(int i=1;i<=n;i++) scan(w[i]);
}
void dfs(int i,int fa,int dep){
if(dep==2){//搜索到第二层先处理然后返回
int t=w[now_p]*w[i];
if(t>max_w) max_w=t;
sum_w=(sum_w+t)%mo;
return;
}
for(int p=first[i];p;p=e[p].next){
int j=e[p].to;
if(j==fa) return;
dfs(j,i,dep+1);
}
}
int main(){
init();//建立数据结构
for(now_p=1;now_p<=n;now_p++) dfs(now_p,-1,0);
printf("%d %d\n",max_w,sum_w*2%mo);
return 0;
}
100分方法:
将枚举深度为二得节点该为枚举节点x所有深度为1的节点,计算每个节点两两相乘即可解决问题2
至于问题1,在x深度为1的节点中找到两个最大的与max_w比较就可以了
现在讨论问题二怎么实现:
1.暴力求和,铁定超时
2.sum1为所有节点权值和,sum2为所有节点权值平方和,它们有什么关系呢,考虑:sum1^2-sum2
如图为sum1*sum1:
显然,每个元素都有一个xi^2(红色),而对xi*xj (i!=j) 有两个(如图,自己思考一下)
所以每个节点两两相乘结果为:sum1^2-sum2
100分代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cctype>
const int maxn=200005;
const int mo=10007;
using namespace std;
struct edge{
int to,next;
}e[maxn<<1];
int first[maxn],np=0,w[maxn],n;
int now_p,max_w=0;
long long sum_w=0;
void scan(int& in){
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
in=0;
while(isdigit(ch)) in=in*10+ch-'0',ch=getchar();
}
void addedge(int u,int v){
e[++np]=(edge){v,first[u]};
first[u]=np;
}
void init(){
int x,y;
scan(n);
for(int i=1;i<n;i++){
scan(x),scan(y);
addedge(x,y);
addedge(y,x);
}
for(int i=1;i<=n;i++) scan(w[i]);
}
void fake_dfs(int i){
int node_sum=0,max1=0,max2=0;
long long sum2=0,sum1=0;
for(int p=first[i];p;p=e[p].next){
node_sum++;
int j=e[p].to;
sum1+=w[j];
sum2+=w[j]*w[j];
if(w[j]>max2){
if(w[j]>max1) max2=max1,max1=w[j];
else max2=w[j];
}
}
if(node_sum<=1) return;
max_w=max(max_w,max1*max2);
long long t=(sum1*sum1-sum2);//计算
sum_w=(t+sum_w)%mo;
}
int main(){
// freopen("tree.txt","r",stdin);
init();//建立数据结构
for(now_p=1;now_p<=n;now_p++) fake_dfs(now_p);
printf("%d %lld\n",max_w,sum_w);
return 0;
}