1. 程式人生 > >NOIP2014 聯合權值

NOIP2014 聯合權值

兩個問題都需要按照每個節點的相鄰點來思考解法。
把符合題目要求的,可產生聯合權值的有序點對,稱為聯合點

  • 第一問
    每個節點的子節點(相鄰點)之間,彼此組成聯合點,具體看圖。

    可以比較每個節點的相鄰點的權值,得到最大子節點和次大子節點。
    他們的聯合權值就是這個節點所能發現的最大聯合權值。
    然後更新答案
ans=max(ans,max1max2)
  • 第二問
    直接兩兩相乘的話能得70分,剩下三個點會TLE
    但其實我們需要的只是乘積的和
    網上有加法分配律的演算法,但我這裡是難一點的…
    設一個點u的相鄰點為a, b, c
    那麼這個點能為答案貢獻
    的值就是這三個點彼此的聯合權值之和的兩倍
    (聯合權值是重複的,如(1,3)和(3,1)都要算上)
    我們要求的就是2(ab+bc+ac)
    這個值可以轉化為:
    (a+b+c)2=a2+b2+c2+2ab+2bc+2ac
    2(ab+bc+ac)=(a+b+c)2(a2+b2+c2)
    ans=(i=1nison)2i=1n(ison)2
    還要注意 如果不開long long 就要邊加邊模
#include <cstdio>
#include <algorithm>
const int maxn = 200010;
int tot=0,last[maxn],value[maxn];
bool vis[maxn];
int max1,max2,u,v,que,n,ans;
struct Edge{
    int u,v,to;
    Edge(){}
    Edge(int u, int v, int to) : u(u), v(v), to(to) {}
}e[maxn*2+10];
void addedge(int u,int v) {
    e[++tot] = Edge(u,v,last
[u]); last[u] = tot; } int main() { scanf("%d",&n); for(int i=1; i<=n-1; i++){ scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u); } for(int i=1; i<=n; i++) scanf("%d",&value[i]); for(int u=1; u<=n; u++) { int s1=0,s2=0,max1=0,max2=0; for(int i=last[u]; i; i=e[i].to) { int v = e[i].v; if(value[v] > max1) max1 = value[v]; else if(value[v] > max2) max2 = value[v]; s1 = (s1%10007 + value[v]%10007) % 10007; s2 = (s2%10007 + value[v]*value[v]%10007) % 10007; } ans = (ans%10007 + (s1*s1 - s2)%10007) % 10007; que = std::max(que,max1*max2); } printf("%d %d",que,ans%10007); return 0; }