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

题目链接:https://nanti.jisuanke.com/t/41403

题目截图:

解题思路:看到题目就可以看出是一道树形DP的题。每次dfs遍历所有的值,维护的当前节点往下有多少种0,1,2的个数和贡献,然后向上递推就好了。而且这种计数的,就需要维护一个相当于前缀和的东西,防止重复计算。最后就要把答案乘个2就是结果了。而且每次dfs遍历的时候都需要计算一下以这个节点为根的经过当前节点的答案有那些。这个可以直接3*3的暴力就好了。

代码如下:

#include<bits/stdc++.h>
#define FRD freopen("in.txt", "r", stdin)
#define __ctz(x) __builtin_ctz(x)               //返回二进制下末尾有几个零
#define __ffs(x) __builtin_ffs(x)               //返回二进制下最后一个1的位置,从1开始
#define __max_elenment *max_element				//返回一个数组的最大值,参数是数组的起始位置和结束位置
#define __min_elenment *min_element				//返回一个数组的最大值,参数是数组的起始位置和结束位置
#define ms(x) memset(x,0,sizeof(x))
#define lowbit(x) x&(-x)
#define pb push_back
#define mk make_pair
#define all(x) x.begin(),x.end()
#define lson (id<<1)
#define rson (id<<1|1)
#define fi first
#define se second
using namespace std;
typedef long long int LL;
typedef pair<LL,LL> pll;
const LL INF = 1e18;
const int N = 1e5+7;
const int MOD = 1e9+7;
constexpr int mul(int a, int b) { return 1LL * a * b % MOD; }
template<typename... T>
constexpr int mul(int a, int b, T... t) { return mul(mul(a, b), t...); }
inline int read() {char ch=getchar();int x=0,f=1;while(ch<'0'||ch>'9') {if(ch == '-') f = -1;ch = getchar();}while('0'<=ch&&ch<='9') {x=x*10+ch-'0';ch=getchar();}return x*f;}
inline LL readl() {char ch=getchar();LL x=0,f=1;while(ch<'0'||ch>'9') {if(ch == '-') f = -1;ch = getchar();}while('0'<=ch&&ch<='9') {x=x*10+ch-'0';ch=getchar();}return x*f;}
int n;
LL cnt[3][N],cost[3][N],ans[3];
struct Node{
	int v;
	LL c;
};
vector<Node> G[N];
void dfs(int u, int fa) {
	LL now_cnt[3], now_cost[3];
	ms(now_cnt); ms(now_cost);
	for (auto e:G[u]) {
		int v = e.v;
		if (v == fa) continue;
		dfs(v,u);
		now_cnt[e.c%3]++;
		now_cost[e.c%3] += e.c;
		for (int j = 0; j < 3; j++) {
			int tmp = (e.c+j)%3;
			now_cnt[tmp] = (now_cnt[tmp]+cnt[j][v])%MOD;
			now_cost[tmp] = (now_cost[tmp]+cost[j][v]+e.c*cnt[j][v])%MOD;
		}
		for (int j = 0; j < 3; j++) {
			cnt[j][v] = now_cnt[j];
			cost[j][v] = now_cost[j];
			now_cnt[j] = now_cost[j] = 0;
		}
		for (int i = 0; i < 3; i++) {
			ans[i] = (ans[i]+cost[i][v]);
			for (int j = 0; j < 3; j++) {
				int tmp = (i+j)%3;
				ans[tmp] = (ans[tmp]+cost[i][v]*cnt[j][u]+cnt[i][v]*cost[j][u])%MOD;
			}
		}
		for (int j = 0; j < 3; j++) {
			cnt[j][u] = (cnt[j][u]+cnt[j][v])%MOD;
			cost[j][u] = (cost[j][u]+cost[j][v])%MOD;
		}
	}
}
int main() {
    #ifndef ONLINE_JUDGE
        int startTime = clock();
        FRD;
    #endif
    while (~scanf("%d",&n)) {
    	for (int i = 1; i < n; i++) {
    		int u = read(), v = read(), c = read();
    		G[u].pb((Node){v,c});
    		G[v].pb((Node){u,c});
		}
		dfs(0,0);
		printf("%lld %lld %lld\n",ans[0]*2%MOD,ans[1]*2%MOD,ans[2]*2%MOD);
		ms(ans);
		for (int i = 0; i < n; i++) {
			G[i].clear();
			for (int j = 0; j < 3; j++) {
				cnt[j][i] = cost[j][i] = 0;
			}
		}
	}
	#ifndef ONLINE_JUDGE
        printf("Time = %dms\n", clock() - startTime);
    #endif
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值