HDU - 4303 Hourai Jeweled

题意:

给定一棵有 n n n 个结点的树,结点、边带权,一条路径的权值非零且为路径上点权和当且仅当该路径上不存在相邻边的边权相同,求所有路径的权值和。 ( n ≤ 3 × 1 0 5 ) (n \leq 3×10^5) (n3×105)

链接:

https://vjudge.net/problem/HDU-4303

解题思路:

第一反应写了点分,对于 c a l cal cal 函数,只需要直接 d f s dfs dfs 求出各子树合法的路径个数以及路径权值和,然后再计算即可。如计算 x x x y y y 两子树合并的答案:
∑ i ∑ j ( x i + y j ) = ∑ i ( n u m y ∗ x i + s u m y ) = n u m y ∗ s u m x + s u m y ∗ n u m x \sum\limits_{i}\sum\limits_{j}(x_i + y_j)=\sum\limits_{i}(num_y * x_i + sum_y)=num_y*sum_x+sum_y*num_x ij(xi+yj)=i(numyxi+sumy)=numysumx+sumynumx
树形 d p dp dp 也可,子树信息可向上合并。

参考代码:

点分治:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 3e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

typedef pair<ll, ll> pll;
vector<pii> G[maxn];
int a[maxn], siz[maxn], vis[maxn], rub[maxn];
pll mp[maxn];
int n, tn, rmn, rt, top;
ll tmpx, tmpy, ans;

void getRt(int u, int f){

    int mx = 0; siz[u] = 1;
    for(auto &e : G[u]){

        int v = e.second, w = e.first;
        if(v == f || vis[v]) continue;
        getRt(v, u);
        siz[u] += siz[v];
        mx = max(mx, siz[v]);
    }
    mx = max(mx, tn - siz[u]);
    if(mx < rmn) rmn = mx, rt = u;
}

void dfs(int u, int f, ll y, int pre){

    ++tmpx, tmpy += y;
    for(auto &e : G[u]){

        int v = e.second, w = e.first;
        if(v == f || vis[v] || w == pre) continue;
        dfs(v, u, y + a[v], w);
    }
}

void cal(int u){

    ll sumx = 1, sumy = 0;
    for(auto &e : G[u]){

        int v = e.second, w = e.first;
        if(vis[v]) continue;
        tmpx = tmpy = 0;
        dfs(v, u, a[v], w);
        // cout << u << " " << tmpx << " " << tmpy << " " << sumx << " " << sumy << " " << ans << endl;
        ans += tmpx * sumy + tmpy * sumx + tmpx * sumx * a[u];
        ans -= tmpx * mp[w].second + tmpy * mp[w].first + tmpx * mp[w].first * a[u];
        mp[w].first += tmpx, mp[w].second += tmpy;
        sumx += tmpx, sumy += tmpy;
        rub[++top] = w;
    }
    while(top) mp[rub[top--]] = {0, 0};
}

void dfz(int u){

    vis[u] = 1;
    cal(u);
    for(auto &e : G[u]){

        int v = e.second, w = e.first;
        if(vis[v]) continue;
        tn = siz[v], rmn = inf, getRt(v, u);
        dfz(rt);
    }
    vis[u] = 0;
}

int main(){

    ios::sync_with_stdio(0); cin.tie(0);
    while(cin >> n){

        ans = 0;
        for(int i = 1; i <= n; ++i){

            G[i].clear();
        }
        for(int i = 1; i <= n; ++i) cin >> a[i];
        for(int i = 1; i < n; ++i){

            int u, v, w; cin >> u >> v >> w;
            G[u].pb({w, v}), G[v].pb({w, u});
        }
        tn = n, rmn = inf, getRt(1, 0);
        dfz(rt);
        cout << ans << endl;
    }
    return 0;
}

 
树形 d p dp dp

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 3e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

typedef pair<ll, ll> pll;
vector<pii> G[maxn];
map<int, pll> dp[maxn];
int a[maxn];
int n; ll ans;

void dfs(int u, int f){

    dp[u].clear();
    dp[u][0] = {1, a[u]};
    pll sum = {1, a[u]};
    for(auto &e : G[u]){
    
        int v = e.second, w = e.first;
        if(v == f) continue;
        dfs(v, u);
        for(auto &it : dp[v]){

            if(it.first == w) continue;
            pll tmp = (pll){it.second.first, it.second.second + it.second.first * a[u]};
            ans += tmp.first * sum.second + tmp.second * sum.first - tmp.first * sum.first * a[u];
            ans -= tmp.first * dp[u][w].second + tmp.second * dp[u][w].first - tmp.first * dp[u][w].first * a[u];
            // cout << u << " " << v << " " << tmp.first << " " << tmp.second << " " << sum.first << " " << sum.second << " " << ans << endl;
        }
        for(auto &it : dp[v]){

            if(it.first == w) continue;
            pll tmp = (pll){it.second.first, it.second.second + it.second.first * a[u]};
            dp[u][w].first += tmp.first, dp[u][w].second += tmp.second;
            sum.first += tmp.first, sum.second += tmp.second;
        }
    }
}

int main(){

    ios::sync_with_stdio(0); cin.tie(0);
    while(cin >> n){

        ans = 0;
        for(int i = 1; i <= n; ++i){

            G[i].clear();
        }
        for(int i = 1; i <= n; ++i) cin >> a[i];
        for(int i = 1; i < n; ++i){

            int u, v, w; cin >> u >> v >> w;
            G[u].pb({w, v}), G[v].pb({w, u});
        }
        dfs(1, 0);
        cout << ans << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值