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 ? '\n' : ' '); } } 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\n",f[1][0],f[1][1],f[1][2]); } return 0; } /** 3 0 1 2 0 2 3 */