[树形DP]SCOI2019:RGB

传送门

一个容斥的做法,对于树上连通块问题,转化为点的贡献之和减去边的贡献之和
所以这题就简单了,统计所有绿点的贡献和边的两端都是绿点的边的贡献

Code:

#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
inline int add(int x,int y){x+=y;if(x>=mod) x-=mod;return x;}
inline int dec(int x,int y){x-=y;if(x<0) x+=mod;return x;}
inline int mul(int x,int y){return (ll)x*y%mod;}
inline void inc(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void Dec(int &x,int y){x-=y;if(x<0) x+=mod;}
inline void Mul(int &x,int y){x=mul(x,y);}
const int N=2e3+5;
int vis[N<<1],nxt[N<<1],head[N],c[N<<1],tot=0;
int rd[N],bl[N],W,col[N];
inline void add(int x,int y,int z){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;c[tot]=z;}
inline void dfs(int v,int fa,int dep){
	if(dep>W) {rd[v]=bl[v]=0;return;}
	pt[v]=(col[v]!=3),pb[v]=(col[u]!=1);
	for(int i=head[v];i;i=nxt[i]){
		int y=vis[i];
		if(y==fa) continue;
		dfs(y,v,dep+c[i]);
		Mul(rd[v],rd[y]+1);Mul(bl[v],bl[y]+1);
	}
}
int ans=0;
inline void point(int x){dfs(x,0,0);inc(ans,mul(rd[x],bl[x]));}
inline void edge(int x,int y,int z){dfs(x,y,z);dfs(y,x,z);Dec(ans,mul(mul(rd[x],bl[x]),mul(rd[y],bl[y])));}
struct E{int x,y,z;}e[N];
int main(){
	int n=read();W=read();
	for(int i=1;i<=n;i++){
		char ch=getchar();
		if(ch=='R') col[i]=1;
		if(ch=='G') col[i]=2;
		if(ch=='B') col[i]=3;
	}
	for(int i=1;i<n;i++){
		e[i].x=read(),e[i].y=read(),e[i].z=read();
		add(e[i].x,e[i].y,e[i].z);add(e[i].y,e[i].x,e[i].z);
	}
	for(int i=1;i<=n;i++) if(col[i]==2) point(i);
	for(int i=1;i<n;i++) if(col[e[i].x]==2 && col[e[i].y]==2) edge(e[i].x,e[i].y,e[i].z);
	cout<<ans;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值