技巧题
题目意思很简单,求两个距离为2的点的点权。可以转化为求一个点其中两条出边的点权。
刚开始写DFS,然后华丽丽地T掉了。。。以为哪里写挂了,算了一下发现是 O(n2) O ( n 2 ) 的。。。于是重新想算法
后来发现一个公式:
2∗(a[1]∗a[2]+a[2]∗a[3]+a[1]∗a[3])=(a[1]+a[2]+a[3])2−(a[1]2+a[2]2+a[3]2)
2
∗
(
a
[
1
]
∗
a
[
2
]
+
a
[
2
]
∗
a
[
3
]
+
a
[
1
]
∗
a
[
3
]
)
=
(
a
[
1
]
+
a
[
2
]
+
a
[
3
]
)
2
−
(
a
[
1
]
2
+
a
[
2
]
2
+
a
[
3
]
2
)
,
它的拓展式也是成立的。(证明的话展开化简即可)
于是我们只需要遍历一遍就行了,算值的时候把这个东西代进去。求最大值就是求前二大值的积。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 200000
#define DALAO 10007
using namespace std;
typedef long long LL;
struct edge{
int next,to;
};
int n,k;
LL sum,ans;
int h[MAXN+5],w[MAXN+5];
edge ed[MAXN*2+5];
inline char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
if (l==r) return EOF; return *l++;
}
inline int _read(){
int num=0; char ch=readc();
while (ch<'0'||ch>'9') ch=readc();
while (ch>='0'&&ch<='9') { num=num*10+ch-48; ch=readc(); }
return num;
}
void addedge(int x,int y){
ed[++k].next=h[x]; ed[k].to=y; h[x]=k;
}
void calc(int x){
LL s1=0,s2=0,t1=0,t2=0;
for (int i=h[x];i;i=ed[i].next){
int v=ed[i].to;
s1+=w[v]; s2+=w[v]*w[v];
if (t1<w[v]) t1=w[v];
else if (w[v]>t2) t2=w[v];
}
s1*=s1; ans=max(ans,t1*t2); sum=(((s1-s2)%DALAO+sum)%DALAO)%DALAO;
}
int main(){
n=_read();
for (int i=1;i<n;i++){
int u=_read(),v=_read();
addedge(u,v); addedge(v,u);
}
for (int i=1;i<=n;i++) w[i]=_read();
for (int i=1;i<=n;i++) calc(i);
printf("%lld %lld\n",ans,sum%DALAO);//其实并不需要long long
return 0;
}