标签:复杂度 spl 红色 main 集合 cto turn str stdout
在点数为\(N\)的树上,每个点有各自的颜色(红色、绿色或蓝色),每条边有各自的长度。
你的任务是计算点集对\((U,V)\)的数量,满足:
集合\(U\)内的点均为红色或绿色,集合\(V\)内的点均为绿色或蓝色;
集合\(U\)和集合\(V\)都是连通的(若集合内任意两点间的简单路径上的点都属于该集合,则称该集合是连通的);
存在一个既属于集合\(U\)又属于集合\(V\)的点\(x\),使得对于任意一个属于集合\(U\)或集合\(V\)的点\(y\),满足点\(x\)和点\(y\)的距离不超过\(M\)(两点之间的距离即为它们之间的简单路径上的边的长度之和)。
答案对\(10^9+7\)取模。
对于所有数据点,\(N≤2000\)。
https://blog.csdn.net/sslz_fsy/article/details/101315047
考虑只有一个绿点的情况,就是一个裸的树形DP,强制选当前点
我们可以枚举所有绿点算一遍答案,考虑有哪些情况会算重。
如果绿点不相连,很明显不会重,会重的情况只有绿色点聚成一坨的时候。
我们要让一坨的点的贡献只算一次,发现\(点数-边数=1\)
于是可以枚举每个绿点,在减去每条边的贡献就可以让一个连通块只算一次了。
时间复杂度\(O(N^2)\)。
CO int N=2e3+10;
int W;
char col[N];
struct edge {int u,v,w;} E[N];
vector<edge> 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<int>();read(W);
scanf("%s",col+1);
for(int i=1;i<n;++i){
int u=read<int>(),v=read<int>(),w=read<int>();
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<n;++i)if(col[E[i].u]==‘G‘ and col[E[i].v]==‘G‘) erase(E[i].u,E[i].v,E[i].w);
printf("%d\n",ans);
return 0;
}
标签:复杂度 spl 红色 main 集合 cto turn str stdout
原文地址:https://www.cnblogs.com/autoint/p/12685723.html