1. 程式人生 > >1282:聯合權值

1282:聯合權值

對於每一個點,所有與這個點相連的其他點構成一個集合,這個集合內任意兩個點的距離為2。

很自然地在建圖的過程中得到這些集合。

vector<ll>q[200100];
q[u].push_back(v);
q[v].push_back(u);

 

列舉每個點,如果這個點相應的集合裡的元素數量大於1,這個集合就對答案有貢獻。

對於每個這樣的集合,可以用一個for迴圈遍歷所有元素,記錄權值總和sum。

令sum=sum*sum,相當於分別把 每個點的權值 乘 所有點的權值總和 ,然後相加。sum=(w[1]+w[2]+...w[n])*(w[1]+w[2]+...w[n])。

這樣會出現自己乘自己的情況,去掉。即減去w[1]*w[1]+w[2]*w[2],...+w[n]*w[n],可用一個變數記錄這些的和。 

 

題目還讓求最大權值乘積,可以定義一個變數maxx遇到大的不斷更新。如何更新?我是在  for迴圈遍歷所有元素記錄權值總和sum  的時候,定義兩個變數b1,b2,表示集合中最大和次大的權值。

if(w[q[i][j]]>=b1){b2=b1;b1=w[q[i][j]];} 	//w表示權值,q表示第i個點的集合中第j個元素。			

else if(w[q[i][j]]>=b2){b2=w[q[i][j]];}

 for迴圈完成後

maxx=max(maxx,b2*b1);

 

 當時寫的時候也擔心會不會超時,因為用了兩個for迴圈,第一層遍歷每個點 ,n ≤ 200,000,第二層遍歷每個集合中的點。

   會不會出現點很多 而且 每個集合中的點 也很多的情況。    

   後來想了想,耗時的部分在於遍歷每個集合的所有元素。這是個樹形結構,有n-1條邊,一個點如果能成為 其他點的集合內的元素,需要一條邊,邊是無向的,一條邊可以讓兩個點成為其他點的集合內的元素,有n-1條邊,所以所有集合的元素數量之和不會超過2*(n-1),不會超時。

 

#include<bits/stdc++.h>
#define ll long long
#define scl(x) scanf("%lld",&x)
#define sc(x) scanf("%ld",&x)
using namespace std;
ll w[200100];
const ll p=10007;
vector<ll>q[200100];
int main()
{
	int n;
	sc(n);
	ll u,v;
	for(int i=1;i<n;i++)
	{
		scl(u);
		scl(v);
		q[u].push_back(v);
		q[v].push_back(u);
	}
	
	for(int i=1;i<=n;i++)
	scl(w[i]);
	ll ans=0;
	ll maxx=-1;
	ll b1,b2;
	for(int i=1;i<=n;i++)
	if(q[i].size()>1)
	{
		b1=0;b2=0;
		ll ans1=0; 
		ll ans2=0;
		for(int j=0;j<q[i].size();j++)
		{
			 ans1+=w[q[i][j]];
			 ans1%=p;
			 ans2+=w[q[i][j]]*w[q[i][j]];
			 ans2%=p;
			 if(w[q[i][j]]>=b1){b2=b1;b1=w[q[i][j]];} 				
			 else if(w[q[i][j]]>=b2){b2=w[q[i][j]];}	
		}
		ans1*=ans1;
		ans1%=p;
		ans1-=ans2;
		if(ans1<0)ans1+=p;
		maxx=max(maxx,b2*b1);
		ans+=ans1;
		ans%=p;
	}
	printf("%lld %lld\n",maxx,ans);
}