1282:聯合權值
阿新 • • 發佈:2018-11-10
對於每一個點,所有與這個點相連的其他點構成一個集合,這個集合內任意兩個點的距離為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); }