「SCOI 2019 D2T2」RGB

传送门


problem

有一棵树, n n n 个节点,每条边有边权 c i c_i ci,每个点的颜色为 R,G,B 三种颜色之一(R 为红色,G 为绿色,B 为蓝色)。

你要统计有序对 ( U , V ) (U,V) (U,V) 的数量,其中 U , V U,V U,V 是满足以下条件的两个点集:

  1. U U U V V V 都必须是连通的;
  2. U U U 的颜色只能是红色或者绿色, V V V 的颜色只能是绿色或者蓝色;
  3. 存在一个 U , V U,V U,V 均包含的点 x x x,使得对于所有在 U , V U,V U,V 集合中的点 y y y,有 d i s ( x , y ) ≤ w dis(x,y)≤w dis(x,y)w

答案对 1 0 9 + 7 10^9+7 109+7 取模。

数据范围: n ≤ 2000 n≤2000 n2000 w ≤ 1 0 7 w≤10^7 w107 c i ≤ 1 0 6 c_i≤10^6 ci106


solution

听说这是一道论文题。

我们先考虑只有一个绿点的时候怎么做。

考虑把绿点当作根,然后树形 d p dp dp。设 R ( x ) \texttt R(x) R(x) 表示强制选 x x x,以 x x x 为根的子树中只有红点和绿点的连通块个数。 B ( x ) \mathtt B(x) B(x) 的定义与之类似:

R ( x ) = ∏ v ∈ s o n ( x ) ( R ( v ) + 1 ) B ( x ) = ∏ v ∈ s o n ( x ) ( B ( v ) + 1 ) \begin{aligned} \mathtt R(x)&=\prod_{v\in son(x)}(\texttt R(v)+1)\\ \mathtt B(x)&=\prod_{v\in son(x)}(\texttt B(v)+1) \end{aligned} R(x)B(x)=vson(x)(R(v)+1)=vson(x)(B(v)+1)

意思就是, x x x 的儿子 v v v 都有 R ( v ) + 1 \texttt R(v)+1 R(v)+1 种选择( + 1 +1 +1 是因为可以不选),由于强制选 x x x,用乘法原理乘一下就是答案。

那么答案就是 R ( r ) × B ( r ) \texttt R(r)\times \texttt B(r) R(r)×B(r) r r r 就是那个作为根的绿点。

那么如果有几个绿点是连通的,答案会被算重,考虑如果减去重复的部分。

这时想到,对于连通的一些绿点,点数减去边数等于 1 1 1。因此我们可以先枚举点加上,再枚举边减去

时间复杂度 O ( n 2 ) O(n^2) O(n2)


code

#include<bits/stdc++.h>
using namespace std;
const int N=4005,P=1e9+7;
int n,lim,t,ans,col[N];
int first[N],v[N],w[N],nxt[N];
struct edges{int u,v,w;}e[N];
int add(int x,int y)  {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y)  {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y)  {return 1ll*x*y%P;}
void edge(int x,int y,int z){
	nxt[++t]=first[x],first[x]=t,v[t]=y,w[t]=z;
}
int R[N],B[N];
void dfs(int x,int fa,int dis){
	if(dis>lim) {R[x]=B[x]=0;return;}
	R[x]=(col[x]!=3),B[x]=(col[x]!=1);
	for(int i=first[x];i;i=nxt[i]){
		int to=v[i];
		if(to==fa)  continue;
		dfs(to,x,dis+w[i]);
		R[x]=mul(R[x],R[to]+1);
		B[x]=mul(B[x],B[to]+1);
	}
}
void calc(int x){
	dfs(x,0,0);
	ans=add(ans,mul(R[x],B[x]));
}
void calc(int u,int v,int w){
	dfs(u,v,w),dfs(v,u,w);
	ans=dec(ans,mul(mul(R[u],B[u]),mul(R[v],B[v])));
}
char S[N];
int main(){
	scanf("%d%d%s",&n,&lim,S+1);
	for(int i=1;i<=n;++i){
		if(S[i]=='R')  col[i]=1;
		if(S[i]=='G')  col[i]=2;
		if(S[i]=='B')  col[i]=3;
	}
	for(int i=1,x,y,z;i<n;++i){
		scanf("%d%d%d",&x,&y,&z);
		edge(x,y,z),edge(y,x,z),e[i]=(edges){x,y,z};
	}
	for(int i=1;i<=n;++i)  if(col[i]==2)  calc(i);
	for(int i=1;i<n;++i){
		int u=e[i].u,v=e[i].v,w=e[i].w;
		if(col[u]==2&&col[v]==2)  calc(u,v,w);
	}
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
中描述了一个幼儿园里分配糖果的问题,每个小朋友都有自己的要求。问题的输入包括两个整数NN和KK,表示幼儿园里的小朋友数量和要满足的要求数量。接下来的KK行表示小朋友们的要求,每行有三个数字,XX,AA,BB。如果X=1,表示第AA个小朋友分到的糖果必须和第BB个小朋友分到的糖果一样多;如果X=2,表示第AA个小朋友分到的糖果必须少于第BB个小朋友分到的糖果;如果X=3,表示第AA个小朋友分到的糖果必须不少于第BB个小朋友分到的糖果;如果X=4,表示第AA个小朋友分到的糖果必须多于第BB个小朋友分到的糖果;如果X=5,表示第AA个小朋友分到的糖果必须不多于第BB个小朋友分到的糖果。这个问题可以被看作是一个差分约束系统的问题。 具体地说,可以使用差分约束系统来解决这个问题。差分约束系统是一种通过给变量之间的关系添加约束来求解最优解的方法。对于这个问题,我们需要根据小朋友们的要求建立约束条件,并通过解决这个约束系统来得出最小的糖果数量。 在问题的输入中,X的取值范围为1到5,分别对应不同的关系约束。根据这些约束,我们可以构建一个差分约束图。图中的节点表示小朋友,边表示糖果数量的关系。根据不同的X值,我们可以添加相应的边和权重。然后,我们可以使用SPFA算法(Shortest Path Faster Algorithm)来求解这个差分约束系统,找到满足所有约束的最小糖果数量。 需要注意的是,在读取输入时需要判断X和Y是否合法,即是否满足X≠Y。如果X=Y,则直接输出-1,因为这种情况下无法满足约束条件。 综上所述,为了满足每个小朋友的要求,并且满足所有的约束条件,我们可以使用差分约束系统和SPFA算法来求解这个问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [【差分约束系统】【SCOI2011】糖果 candy](https://blog.csdn.net/jiangzh7/article/details/8872699)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [P3275 [SCOI2011]糖果(差分约束板子)](https://blog.csdn.net/qq_40619297/article/details/88678605)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值