Loading

[解题记录] P1351 [NOIP2014 提高组] 联合权值

P1351 [NOIP2014 提高组] 联合权值

解题思路

我们可以从\(2\)入手,不难发现,和一个点\(u\)距离为\(2\)的点有三类

  1. \(u\)父亲的父亲
  2. \(u\)儿子的儿子
  3. \(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\)......

posted @ 2021-10-28 11:07  Miraii  阅读(54)  评论(0)    收藏  举报