[解题记录] P1351 [NOIP2014 提高组] 联合权值
P1351 [NOIP2014 提高组] 联合权值
解题思路
我们可以从\(2\)入手,不难发现,和一个点\(u\)距离为\(2\)的点有三类
- \(u\)父亲的父亲
- \(u\)儿子的儿子
- \(u\)的父亲的其他儿子
我们其实可以合并\(2,3\),然后直接在算\(1\)的时候算两次
我们可以先以\(1\)为根节点,预处理出所有点的父亲
void getfa(int u,int fa){
f[u]=fa;
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(v==fa) continue;
getfa(v,u);
}
}
然后稍微预处理一下,求出每个父亲的直系儿子的和以及最大最小值
要注意的是,不能只存最大值,还要存次大值,因为如果最大值是他自己就不能和自己相乘
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <ctime>
#define int long long
using namespace std;
const int N=6e5+10;
const int mod=1e4+7;
int ver[N],tot,head[N],nxt[N],val[N],n,m,res,w[N],maxn,maxx[N][3],f[N],sum[N],data[N][4],ans;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
inline void add(int x,int y){
ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;
}
void getfa(int u,int fa){
f[u]=fa;
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(v==fa) continue;
getfa(v,u);
}
}
void pre(int u,int fa){
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(v==fa) continue;
sum[u]=(sum[u]+w[v])%mod;
if(w[v]>maxx[u][0]){maxx[u][0]=w[v],data[u][0]=v;}
else if(w[v]>maxx[u][1]){maxx[u][1]=w[v],data[u][1]=v;}
}
}
signed main(){
n=read();
for(int i=1;i<n;i++){
int x=read(),y=read();
add(x,y);
add(y,x);
}
for(int i=1;i<=n;i++) w[i]=read();
sum[0]=w[1];
getfa(1,0);
for(int i=1;i<=n;i++) pre(i,f[i]);
for(int i=1;i<=n;i++){
if(f[f[i]]) ans=(ans+2*w[i]*w[f[f[i]]])%mod;
if(f[f[i]]) maxn=max(maxn,w[i]*w[f[f[i]]]);
ans=(ans+(sum[f[i]]-w[i])*w[i])%mod;
if(data[f[i]][0]!=i) maxn=max(w[i]*maxx[f[i]][0],maxn);
else maxn=max(w[i]*maxx[f[i]][1],maxn);
}
printf("%lld %lld\n",maxn,ans);
return 0;
}
还是有点思维难度的,不看题解只会\(60pts\)......

浙公网安备 33010602011771号