SCOI2019 RGB

RGB

在点数为\(N\)的树上,每个点有各自的颜色(红色、绿色或蓝色),每条边有各自的长度。

你的任务是计算点集对\((U,V)\)的数量,满足:

  1. 集合\(U\)内的点均为红色或绿色,集合\(V\)内的点均为绿色或蓝色;

  2. 集合\(U\)和集合\(V\)都是连通的(若集合内任意两点间的简单路径上的点都属于该集合,则称该集合是连通的);

  3. 存在一个既属于集合\(U\)又属于集合\(V\)的点\(x\),使得对于任意一个属于集合\(U\)或集合\(V\)的点\(y\),满足点\(x\)和点\(y\)的距离不超过\(M\)(两点之间的距离即为它们之间的简单路径上的边的长度之和)。

答案对\(10^9+7\)取模。

对于所有数据点,\(N≤2000\)

题解

https://blog.csdn.net/sslz_fsy/article/details/101315047

考虑只有一个绿点的情况,就是一个裸的树形DP,强制选当前点

\[f_R(u)=\prod (f_R(v)+1)\\ f_B(u)=\prod (f_B(v)+1)\\ ans=f_R(u)f_B(u) \]

我们可以枚举所有绿点算一遍答案,考虑有哪些情况会算重。

如果绿点不相连,很明显不会重,会重的情况只有绿色点聚成一坨的时候。

我们要让一坨的点的贡献只算一次,发现\(点数-边数=1\)

于是可以枚举每个绿点,在减去每条边的贡献就可以让一个连通块只算一次了。

时间复杂度\(O(N^2)\)

CO int N=2e3+10;
int W;
char col[N];
struct edge {int u,v,w;} E[N];
vector to[N];
int fR[N],fB[N],ans;

void dfs(int u,int fa,int dis){
	if(dis>W) {fR[u]=fB[u]=0; return;}
	if(col[u]!='R') fB[u]=1;
	if(col[u]!='B') fR[u]=1;
	for(int i=0;i<(int)to[u].size();++i){
		int v=to[u][i].v;
		if(v==fa) continue;
		dfs(v,u,dis+to[u][i].w);
		fB[u]=mul(fB[u],fB[v]+1);
		fR[u]=mul(fR[u],fR[v]+1);
	}
}
IN void insert(int u){
	dfs(u,0,0);
	ans=add(ans,mul(fR[u],fB[u]));
}
IN void erase(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(){
	freopen("RGB.in","r",stdin),freopen("RGB.out","w",stdout);
	int n=read();read(W);
	scanf("%s",col+1);
	for(int i=1;i(),v=read(),w=read();
		E[i]=(edge){u,v,w};
		to[u].push_back((edge){u,v,w}),to[v].push_back((edge){v,u,w});
	}
	for(int i=1;i<=n;++i)if(col[i]=='G') insert(i);
	for(int i=1;i

你可能感兴趣的:(SCOI2019 RGB)