1. 程式人生 > >題解【luogup1351 NOIp提高組2014 聯合權值】

題解【luogup1351 NOIp提高組2014 聯合權值】

define 題意 一個點 clu show truct lib ace can

題目鏈接


題意:給定一個無根樹,每個點有一個權值。若兩個點 \(i,j\) 之間距離為\(2\),則有聯合權值 \(w_i \times w_j\)。求所有的聯合權值的和與最大值

分析

  • 暴力求,每個節點遍歷一遍周圍的點,對每個點再遍歷一次
  • 可以拿到70分
  • 考慮正解。對於一個點\(u\),周圍一圈可以到達的點中,從中任選兩個不同的點\(i,j\),則這兩個點構成聯合權值。
  • 所以我們對一個點維護三個值:周圍一圈點\(w_i\)之和\(sumw_u\)\(w_i\)的最大值\(first_u\)\(w_i\)的次大值\(second_u\)
  • 則在\(u\)周圍一圈選取一個點\(i\)與圈上的其他點的所有聯合權值之和為\(w_i \times (sumw_u-w_i)\)
    ;對於\(u\)周圍一圈聯合權值最大值為\(first_u \times second_u\)
  • 用dfs求一遍就行了

實現

  • 第一遍dfs求每個點的三個值
  • 第二遍求答案
  • 復雜度\(O(n)\)

註意事項

  • 取膜!
  • long long!

代碼

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;
#define int long long
const int MAXN = 200200;
const int
MOD = 10007; int n, w[MAXN], cnt, Max = 0, ans, sumw[MAXN], vis1[MAXN], vis2[MAXN], first[MAXN], second[MAXN]; struct edge { int v; edge *next; }pool[MAXN << 1], *head[MAXN]; inline void addedge(int u, int v) { edge *p = &pool[++cnt], *q = &pool[++cnt]; p->v = v, p->next = head[u]; head[u] = p; q->v = u, q->next = head[v]; head[v] = q; } void
dfs1(int u) { vis1[u] = 1; for(edge *p = head[u]; p; p = p->next) { sumw[u] += w[p->v]; if(first[u] < w[p->v]) second[u] = first[u], first[u] = w[p->v]; else second[u] = max(second[u], w[p->v]); if(!vis1[p->v]) dfs1(p->v); } } void dfs2(int u) { vis2[u] = 1; Max = max(Max, first[u] * second[u]); for(edge *p = head[u]; p; p = p->next) { int lh = sumw[u] * w[p->v] - w[p->v] * w[p->v]; ans = (ans + lh) % MOD; if(!vis2[p->v]) dfs2(p->v); } } #undef int int main() { memset(second, -1, sizeof(second)); scanf("%d", &n); for(int i = 1; i < n; i++) { int u, v; scanf("%d%d", &u, &v); addedge(u, v); } for(int i = 1; i <= n; i++) scanf("%lld", &w[i]); dfs1(1); dfs2(1); printf("%lld %lld", Max, ans); return 0; }

題解【luogup1351 NOIp提高組2014 聯合權值】