SCOI2019 RGB [容斥]

传送门
考虑只有一个绿点的情况,就是一个裸的树形 dp,强制选当前点
f u , r = ∏ ( f v , r + 1 ) , f u , b = ∏ ( f v , b + 1 ) f_{u,r} =\prod (f_{v,r}+1),f_{u,b} =\prod (f_{v,b}+1) fu,r=(fv,r+1),fu,b=(fv,b+1)
a n s = f u , r ∗ f u , b ans=f_{u,r}*f_{u,b} ans=fu,rfu,b
我们可以枚举所有绿点算一遍答案,考虑有哪些情况会算重
如果绿点不相连,很明显不会重,会重的情况只有绿色点聚成一坨的时候
我们要让一坨的点的贡献只算一次,发现点数-边数=1
于是可以枚举每个绿点,在减去每条边的贡献就可以让一个连通块只算一次了

#include<bits/stdc++.h>
#define N 4050
using namespace std;
const int Mod = 1000000007;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)){ cnt = cnt * 10 + (ch-'0'), ch = getchar();}
	return cnt * f;
}
typedef long long ll;
ll add(ll a, ll b){ return (a + b) % Mod;}
ll mul(ll a, ll b){ return (a * b) % Mod;}
int first[N], nxt[N], to[N], w[N], tot;
void add(int x, int y, int z){
	nxt[++tot] = first[x], first[x] = tot, to[tot] = y, w[tot] = z;
}
int n, W, col[N];
struct Edge{ int u, v, w;} E[N];
ll ans, fR[N], fB[N];
void dfs(int u, int fa, int dis){
	if(dis > W){ fR[u] = fB[u] = 0; return;}
	if(col[u] != 1) fB[u] = 1;
	if(col[u] != 3) fR[u] = 1;
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(t == fa) continue;
		dfs(t, u, dis + w[i]);
		fB[u] = mul(fB[u], fB[t] + 1);
		fR[u] = mul(fR[u], fR[t] + 1);
	}
}
void Yolanda(int u){ dfs(u, 0, 0); ans = add(ans, mul(fR[u], fB[u]));}
void FSY(int u, int v, int w){
	dfs(u, v, w); dfs(v, u, w);
	ans = add(ans, Mod - mul(mul(fR[u], fB[u]), mul(fR[v], fB[v])));
}
int main(){
	n = read(), W = read(); string s; cin >> s;
	for(int i = 0; i < n; i++){
		if(s[i] == 'R') col[i+1] = 1;
		if(s[i] == 'G') col[i+1] = 2;
		if(s[i] == 'B') col[i+1] = 3; 
	}
	for(int i = 1; i < n; i++){
		int x = read(), y = read(), z = read();
		add(x, y, z); add(y, x, z);
		E[i] = (Edge){ x, y, z };
	} 
	for(int i = 1; i <= n; i++) if(col[i] == 2) Yolanda(i);
	for(int i = 1; i < n; i++) if(col[E[i].u] == 2 && col[E[i].v] == 2) FSY(E[i].u, E[i].v, E[i].w);
	cout << ans; return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值