• 2019沈阳网赛树形dp


    https://nanti.jisuanke.com/t/41403

    2019沈阳网络赛D题

    树形dp。一棵树,求任意两个点的距离之和。u-v和v-u算两次。两点之间的距离分为三类,模3等于0,1,2三类,最后输出这三类的总和。

    第一种方法。直接累加。遍历到一个点的时候。先计算答案。答案加上所有已经遍历过得点到他的距离之和。同时该点也要加上这个值,同时要加上数量。每次先搜到底统计往下遍历的值,然后回溯的时候统计

    回溯的值。因为每次只计算了之前的点到他的距离之和,所以最后的结果要乘以2,因为u-v与v-u算两次。

    每次加的时候先计算当前点前一个点到他的距离,及0+该边的边权。然后在按0,1,2来累加,如果为零表示没有,就不累加。

    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N = 1e4 + 5;
    const int mod = 1e9 + 7;
    typedef pair<int, ll> pii;
    int n;
    vector<pii> g[N];
    ll ans[4];//结果数组
    struct Node {
        ll valup[4];//递归回溯的时候
        ll valdown[4];//递归往下的时候
        ll cntdown[4], cntup[4];//递归往下与回溯的点数量。
    } node[N];
    
    void dfs(int u, int f) {
        int len = g[u].size();
        for(int i = 0; i < len; i++) {
            int v = g[u][i].first;
            ll w = g[u][i].second;
            if(v == f) continue;
            node[v].valdown[w%3] = (node[v].valdown[w%3] + w) % mod;
            node[v].cntdown[w%3] = (node[v].cntdown[w%3] + 1) % mod;
            ans[w%3] = (ans[w%3] + w) % mod;
            for(int j = 0; j < 3; j++) {
                ll ww = (node[u].valdown[j] + node[u].valup[j]) % mod;
                ll cnt = (node[u].cntdown[j] + node[u].cntup[j]) % mod;
                if(ww == 0) continue;
                ll mo = (j + w) % 3;
                node[v].valdown[mo] = (node[v].valdown[mo] + ww) % mod;
                node[v].valdown[mo] = (node[v].valdown[mo] + w * cnt) % mod;
                node[v].cntdown[mo] = (node[v].cntdown[mo] + cnt) % mod;
                ans[mo] = (ans[mo] + ww) % mod;
                ans[mo] = (ans[mo] + w * cnt) % mod;
            }
            dfs(v, u);
            node[u].valup[w%3] = (node[u].valup[w%3] + w) % mod;
            node[u].cntup[w%3] = (node[u].cntup[w%3] + 1) % mod;
            for(int j = 0; j < 3; j++) {
                ll ww = node[v].valup[j];
                ll cnt = node[v].cntup[j];
                if(ww == 0) continue;
                ll mo = (j + w) % 3;
                node[u].valup[mo] = (node[u].valup[mo] + ww) % mod;
                node[u].valup[mo] = (node[u].valup[mo] + w * cnt) % mod;
                node[u].cntup[mo] = (node[u].cntup[mo] + cnt) % mod;
            }
        }
    }
    
    int main() {
        while(~scanf("%d", &n)) {
            int u, v;
            ll w;
            ans[0] = ans[1] = ans[2] = 0;
            for(int i = 0; i < n; i++) {
                g[i].clear();
                for(int j = 0; j < 4; j++) node[i].valup[j] = node[i].valdown[j] = node[i].cntdown[j] = node[i].cntup[j] = 0;
            }
            for(int i = 1; i < n; i++) {
                scanf("%d%d%lld", &u, &v, &w);
                g[u].push_back(pii(v, w));
                g[v].push_back(pii(u, w));
            }
            dfs(0, -1);
            for(int i = 0; i < 3; i++) {
                printf("%lld%c", (ans[i] * 2) % mod, i == 2 ? '
    ' : ' ');
            }
        }
        return 0;
    }

    算贡献的方法。

    #include <stdio.h>
    #include <string.h>
    using namespace std;
    #define LL long long
    const int N=100010;
    const LL MOD = 1e9 + 7;
    
    int n,root;
    int nex[N],tot,fir[N],to[N],len[N];
    LL f[N][3],g[N][3],num[N][3];
    void build(int x,int y,int z)
    {
        nex[++tot]=fir[x];
        fir[x]=tot;
        to[tot]=y;
        len[tot]=z;
    }
    
    void mo(LL &x) {
        x %= MOD;
        if(x < 0) x += MOD;
    }
    
    void dfs(int x,int fa)
    {
        num[x][0]++;
        for(int i=fir[x];i;i=nex[i])
        {
            int y=to[i];
            if(y==fa)continue;
            dfs(y,x);
            for(int j=0;j<3;++j){
                g[x][(j+len[i])%3]+=(num[y][j]*len[i]+g[y][j]) % MOD;
                num[x][(j+len[i])%3]+=num[y][j];
                f[x][j] += f[y][j];
    
                mo(num[x][(j+len[i])%3]);
                mo(g[x][(j + len[i]) % 3]);
                mo(f[x][j]);
            }
        }
        for(int i=0;i<3;i++)
        {
            f[x][i]+=g[x][i];
            mo(f[x][i]);
        }
        for(int i=fir[x];i;i=nex[i])
        {
            int y=to[i];
            if(y==fa)continue;
            for(int j=0;j<3;++j) {
                int ind = (j-len[i])%3;
                if(ind < 0) ind += 3;
                for(int k=0;k<3;++k) {
                    f[x][(j+k+len[i])%3]+=len[i]*(num[x][j]-num[y][ind]) % MOD *num[y][k] % MOD ;
                    f[x][(j+k+len[i])%3]+=(g[x][j] - g[y][ind] - num[y][ind] * len[i]) % MOD *num[y][k] % MOD ;
                    f[x][(j+k+len[i])%3]+=(num[x][j]-num[y][ind])*g[y][k] % MOD;
                    mo(f[x][(j+k+len[i])%3]);
                }
            }
        }
    }
    
    int main()
    {
    //    freopen("a.in","r",stdin);
        int n;
        while (~scanf("%d",&n)) {
            tot=0;
            memset(fir,0,sizeof(fir));
            memset(f,0,sizeof(f));
            memset(g,0,sizeof(g));
            memset(num,0,sizeof(num));
            for (int i = 1;i < n;i++) {
                int x,y,z;
                scanf("%d%d%d",&x,&y,&z);
                x++;y++;
                build(x,y,z);
                build(y,x,z);
            }
            dfs(1,0);
            printf("%lld %lld %lld
    ",f[1][0],f[1][1],f[1][2]);
    
        }
    
        return 0;
    }
    
    /**
    3
    0 1 2
    0 2 3
    */
  • 相关阅读:
    Linux网络编程必看书籍推荐
    SpringMVC DispatcherServlet初始化过程
    freemarker写select组件(五)
    hdu 1009 FatMouse&#39; Trade
    新IOS编程语言 Swift 新编译器Xcode6
    NoSQL数据库:数据的一致性
    POJ 3041 Asteroids
    POJ 3342 Party at Hali-Bula (树形dp 树的最大独立集 判多解 好题)
    __repr__与__str__
    HDU 4950 Monster(公式)
  • 原文地址:https://www.cnblogs.com/downrainsun/p/11528832.html
Copyright © 2020-2023  润新知