【NOIP2014】 联合权值

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

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

输入格式

第一行包含 1 个整数 n。

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

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

输出格式

输出共 1 行,包含 2 个整数,之间用一个空格隔开,依次为图 G 上联合权值的最大值 和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对10007取余。

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

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

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


思路:乱搞

先想到暴力,然而暴力不好写那,于是想到每个点的出边与其距离为1,所以每个点的距离为1的结果是一定的,也就是说可以像DP一样利用起来,虽然是乘法,但是伟大的乘法分配驴就出现了!

然后统计最大值,一样的道理,不过由于对于一个定点,它去访问另外一个点的时候,预处理的最大值可能是它自己,所以最大值和次大值都需要存储,如果与最大值相等,那么就取次大值没跑了,否则就最大值。

我取模的姿势很挫,所以中途结果可能胡爆炸int,用了longlong,这在赛场上就是红果果的30分啊!!!

总的复杂度:统计权值和与极值,需要约n,然后计算更新需要n

所以最后应该是2n的算法

用了邻接表,这样明显快,用了读入优化,不过在vijos上ha

给你讲个笑话,有人在vijos上,把其他注释掉,只用cin读入不处理,然后最后三个点tle了

代码请食用~


#include<iostream>
#include<cstdio>
#include<cstring>
#include<limits.h>
#include<algorithm>
#include<vector> 
#include<queue>
typedef long long ll;
using namespace std;
const int maxn=200005;
const ll Mod=10007;
ll an;
ll h[maxn]; 
vector<int> g[maxn];
ll mx[3][maxn];
ll sm[maxn];
void git(ll &iii)
{
	iii=0;
int f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){iii=iii*10+ch-'0';ch=getchar();}
if(f==-1) iii=-iii;
}
ll k, t2,n;
ll t1;
ll ans;
int main()
{
	git(n);
	for(int i=1;i<n;i++)
	{
		git(t1);git(t2);
		g[t1].push_back(t2);
		g[t2].push_back(t1);
	}
	for(int i=1;i<=n;i++)
	git(h[i]);
	for(int i=1;i<=n;i++)
	{
		if(g[i].empty())
		continue;
		int lm=g[i].size();
		int f1=0,f2=0;
		ll res=0;
		for(int j=0;j<lm;j++)
		{
			res+=h[g[i][j]];
			if(h[g[i][j]]>f1)
			{
				f2=f1;
				f1=h[g[i][j]];
			}
			else
			if(h[g[i][j]]==f1)
			{
				f2=h[g[i][j]];
			}
			else
			if(h[g[i][j]]>f2)
			{
				f2=h[g[i][j]];
			}
		}
		mx[0][i]=f1;
		mx[1][i]=f2;
		sm[i]=res;
	}
	for(int i=1;i<=n;i++)
	{
		if(g[i].empty())
		continue;
		int lm=g[i].size();
		for(int j=0;j<lm;j++)
		{
			int nw=g[i][j];
			ans+=h[i]*(sm[nw]-h[i]);
			ans%=Mod;
			if(mx[0][nw]==h[i])
			{
				an=max(an,mx[1][nw]*h[i]);
			}
			else
			an=max(an,mx[0][nw]*h[i]);
		}
	}
	cout<<an<<" "<<ans<<endl;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值