Fish eating fruit 沈阳网络赛(树形dp)

Fish eating fruit

\[
Time Limit: 1000 ms \quad Memory Limit: 262144 kB
\]

题意

大体的题意就是给出一棵树,求每一对点之间的距离,然后把该距离存在距离 \(\mod 3\) 的位置,输出总和。

思路

令两个 \(dp\) 数组和两个辅助 \(dp\) 的数组。
\(dp1[i][j]\) 表示从 \(i\) 为起点往下到各个点距离 \(\mod 3\) 后为 \(j\) 的距离总和。
\(cnt1[i][j]\) 表示以 \(i\) 为起点往下到各个点距离 \(\mod 3\) 后为 \(j\) 的节点个数。
\(dp2[i][j]\) 表示从 \(i\) 起点往上一步后到各个点距离 \(\mod 3\) 后为 \(j\) 的距离总和。
\(cnt2[i][j]\) 表示以 \(i\) 为起点往上一步后到各个点距离 \(\mod 3\) 后为 \(j\) 的节点个数。



对于两个 \(dp\) 分别跑一遍 \(dfs\)



对于 \(dp1\) 比较好处理,直接往下 \(dfs\)
以 \(u\) 开始的答案等于从 \(v\) 开始的答案加上这一条边 \(w\) 的贡献,可以得到
\[
dp1[u][(j+w)\%3] = \sum (dp1[v][j] + cnt1[v][j]*w)\ cnt1[u][(j+w)\%3] = \sum cnt1[v][j]
\]



对于 \(dp2\) 会比较麻烦,需要用 \(fa\) 节点向上的贡献在加上 \(fa\) 节点往下的贡献在减去 \(fa\) 节点往 \(u\) 走的贡献。这些节点就是 \(u\) 往上走一步后可以走到的所有节点。这样算出真实的节点数和距离总和,然后 \(u\) 才能开始转移。
设 \(faw\) 为从 \(u\) 到 \(fa\) 的路径长度
计算真实的节点数:
\[
c[j] = cnt2[fa][j]+cnt1[fa][j]\ c[(j+faw)\%3] -= cnt1[u][j]
\]
计算真实的距离总和:
\[
d[j] = dp2[fa][j]+dp1[fa][j] \ d[(j+faw)\%3] -= dp1[u][0]+cnt1[u][j]*faw
\]
则最后的 \(dp2\) 就可以利用 \(d\) 和 \(c\) 得到了
\[
dp2[u][(j+faw)\%3] = c[j]*faw+d[j] \ cnt2[u][(j+faw)\%3] = c[j]
\]

/***************************************************************
    > File Name    : a.cpp
    > Author       : Jiaaaaaaaqi
    > Created Time : Mon 16 Sep 2019 08:55:33 PM CST
 ***************************************************************/

#include <map>
#include <set>
#include <list>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#include <cfloat>
#include <string>
#include <vector>
#include <cstdio>
#include <bitset>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
#define  lowbit(x)  x & (-x)
#define  mes(a, b)  memset(a, b, sizeof a)
#define  fi         first
#define  se         second
#define  pb         push_back
#define  pii        pair<int, int>

typedef unsigned long long int ull;
typedef long long int ll;
const int    maxn = 1e5 + 10;
const int    maxm = 1e5 + 10;
const ll     mod  = 1e9 + 7;
const ll     INF  = 1e18 + 100;
const int    inf  = 0x3f3f3f3f;
const double pi   = acos(-1.0);
const double eps  = 1e-8;
using namespace std;

int n, m;
int cas, tol, T;

vector< pii > vv[maxn];
ll cnt1[maxn][3], cnt2[maxn][3];
ll dp1[maxn][3], dp2[maxn][3];

void dfs1(int u, int fa) {
    cnt1[u][0] = 1;
    for(auto i : vv[u]) {
        int v = i.fi, w = i.se;
        if(v == fa) continue;
        dfs1(v, u);
        dp1[u][(0+w)%3] += (cnt1[v][0]*w%mod+dp1[v][0])%mod;
        dp1[u][(1+w)%3] += (cnt1[v][1]*w%mod+dp1[v][1])%mod;
        dp1[u][(2+w)%3] += (cnt1[v][2]*w%mod+dp1[v][2])%mod;
        for(int j=0; j<3; j++)  dp1[u][j] %= mod;
        cnt1[u][(0+w)%3] += cnt1[v][0];
        cnt1[u][(1+w)%3] += cnt1[v][1];
        cnt1[u][(2+w)%3] += cnt1[v][2];
    }
}

void dfs2(int u, int fa) {
    if(u!=1) {
        int faw;
        for(auto i : vv[u]) {
            if(i.fi == fa) {
                faw = i.se;
                break;
            }
        }
        int c[3] = { 0 };
        c[0] = cnt2[fa][0]+cnt1[fa][0];
        c[1] = cnt2[fa][1]+cnt1[fa][1];
        c[2] = cnt2[fa][2]+cnt1[fa][2];
        c[(0+faw)%3] -= cnt1[u][0];
        c[(1+faw)%3] -= cnt1[u][1];
        c[(2+faw)%3] -= cnt1[u][2];
        ll d[3] = { 0 };
        d[0] = (dp2[fa][0]+dp1[fa][0])%mod;
        d[1] = (dp2[fa][1]+dp1[fa][1])%mod;
        d[2] = (dp2[fa][2]+dp1[fa][2])%mod;
        d[(0+faw)%3] = ((d[(0+faw)%3] - (cnt1[u][0]*faw%mod+dp1[u][0])%mod+mod)%mod+mod)%mod;
        d[(1+faw)%3] = ((d[(1+faw)%3] - (cnt1[u][1]*faw%mod+dp1[u][1])%mod+mod)%mod+mod)%mod;
        d[(2+faw)%3] = ((d[(2+faw)%3] - (cnt1[u][2]*faw%mod+dp1[u][2])%mod+mod)%mod+mod)%mod;

        dp2[u][(0+faw)%3] = (c[0]*faw%mod+d[0])%mod;
        dp2[u][(1+faw)%3] = (c[1]*faw%mod+d[1])%mod;
        dp2[u][(2+faw)%3] = (c[2]*faw%mod+d[2])%mod;
        cnt2[u][(0+faw)%3] += c[0];
        cnt2[u][(1+faw)%3] += c[1];
        cnt2[u][(2+faw)%3] += c[2];
    }
    for(auto i : vv[u]) {
        int v = i.fi, w = i.se;
        if(v == fa) continue;
        dfs2(v, u);
    }
}

int main() {
    // freopen("in", "r", stdin);
    while(~scanf("%d", &n)) {
        for(int i=1; i<=n; i++) {
            vv[i].clear();
        }
        mes(dp1, 0), mes(dp2, 0);
        mes(cnt1, 0), mes(cnt2, 0);
        for(int i=1, u, v, w; i<n; i++) {
            scanf("%d%d%d", &u, &v, &w);
            u++, v++;
            vv[u].pb(make_pair(v, w));
            vv[v].pb(make_pair(u, w));
        }
        dfs1(1, 0);
        dfs2(1, 0);
        // for(int i=1; i<=n; i++) {
        //     for(int j=0; j<3; j++) {
        //         printf("dp1[%d][%d] = %lld, cnt1[%d][%d] = %lld\n", i, j, dp1[i][j], i, j, cnt1[i][j]);
        //     }
        // }
        // cout << "-----------------" << endl;
        // for(int i=1; i<=n; i++) {
        //     for(int j=0; j<3; j++) {
        //         printf("dp2[%d][%d] = %lld, cnt2[%d][%d] = %lld\n", i, j, dp2[i][j], i, j, cnt2[i][j]);
        //     }
        // }
        ll ans0, ans1, ans2;
        ans0 = ans1 = ans2 = 0;
        for(int i=1; i<=n; i++) {
            ans0 = (ans0+dp1[i][0]+dp2[i][0])%mod;
            ans1 = (ans1+dp1[i][1]+dp2[i][1])%mod;
            ans2 = (ans2+dp1[i][2]+dp2[i][2])%mod;
        }
        printf("%lld %lld %lld\n", ans0, ans1, ans2);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Jiaaaaaaaqi/p/11530717.html

时间: 2024-07-30 20:32:54

Fish eating fruit 沈阳网络赛(树形dp)的相关文章

HDU 6201 2017沈阳网络赛 树形DP或者SPFA最长路

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6201 题意:给出一棵树,每个点有一个权值,代表商品的售价,树上每一条边上也有一个权值,代表从这条边经过所需要的花费.现在需要你在树上选择两个点,一个作为买入商品的点,一个作为卖出商品的点,当然需要考虑从买入点到卖出点经过边的花费.使得收益最大.允许买入点和卖出点重合,即收益最小值为0. 解法:我们设1为根节点,假设一开始一个人身上的钱为0.我们设dp[i][0]表示从根节点走到i及其子树并中任一点买

hdu 4274 2012长春赛区网络赛 树形dp ***

设定每个节点的上限和下限,之后向上更新,判断是否出现矛盾 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<queue> 7 #include<map> 8 using namespace std; 9 #define MOD 1000000007 10

2015沈阳网络赛1003 Minimum Cut 树链剖分 数组维护前缀和进行区间增减

2015沈阳网络赛1003  Minimum Cut   树链剖分 数组维护前缀和进行区间增减 Minimum Cut Time Limit: 3000/2000 MS (Java/Others)    Memory Limit: 65535/102400 K (Java/Others)Total Submission(s): 0    Accepted Submission(s): 0 Problem Description Given a simple unweighted graph G 

2019ACM-ICPC沈阳网络赛-C-Dawn-K&#39;s water(完全背包模板题)

Dawn-K's water  1000ms 262144K Dawn-K recently discovered a very magical phenomenon in the supermarket of Northeastern University: The large package is not necessarily more expensive than the small package. On this day, Dawn-K came to the supermarket

2019ACM-ICPC沈阳网络赛-K-Guanguan&#39;s Happy water(思维+暴力)

Guanguan's Happy water 4000ms 262144K Rather than drinking happy water, Guanguan loves storing happy water. So he bought a refrigerator and stored a_iai? bottles of cola into it every day. When the storage is finished on the kk-th day, the refrigerat

2019 沈阳网络赛 D Fish eating fruit ( 树形DP)

题目传送门 题意:求一颗树中所有点对(a,b)的路径长度,路径长度按照模3之后的值进行分类,最后分别求每一类的和 分析:树形DP \(dp[i][j]\) 表示以 i 为根的子树中,所有子节点到 i 的路径长度模3等于 j 的路径之和 \(c[i][j]\) 表示以 i 为根的子树中,所有子节点到 i 的路径长度模3等于 j 的点数 \(ok[i][j]\) 表示以 i 为根的子树中,是否有子节点到 i 的路径长度模3等于 j 每次只考虑所有经过根 x 的路径,并且路径的一个端点在 x 的一颗子

2015长春、沈阳网络赛总结

我所说的总结并不是谈什么题目解法之类的东西 这些东西网上有很多题解 说说这两场网赛吧! 这两场我的状态还行,只是xiaodong还没有找到状态,长春赛lucas+中国剩余定理他硬是打了一整场,还是没打出来,版题没打出来确实不该 wzb状态一般,不过看题的能力依然那么厉害 长春赛中,很遗憾的只出了5道题,按当时过题数目,应该是7道德,可是小东的lucas那题没打出来,而我打得后缀数组那题,当顺时针的时候不用看是否是下标最前面的,但是反过来就需要看了,当时想当然的认为不用,交了4发,一直wa到比赛结

D. Fish eating fruit

树形dp 题:https://nanti.jisuanke.com/t/41403 题意:求任意俩点之间距离之和模3后的三个结果的总数(原距离之和) #include<bits/stdc++.h> using namespace std; #define pb push_back typedef long long ll; const int M=1e4+4; const int mod=1e9+7; struct node{ int v; ll w; }; ll C[M][3],S[M][3

2016 年沈阳网络赛---QSC and Master(区间DP)

题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=5900 Problem Description Every school has some legends, Northeastern University is the same. Enter from the north gate of Northeastern University,You are facing the main building of Northeastern Universi