是道不错的题目
Description
link
给定一棵 \(n\) 个点的树,树上的每个点可以涂 \(m\) 种颜色的中的几种
规定相邻的两个点涂的颜色不一样,求方案数
\(n,m \le 5000\)
Solution
定义状态:
\(f_{x,y}\) 为点 \(x\) 涂颜色 \(y\) 的时候的子树里的方案数
那么答案就是
\[\sum_{i=1\&\&i\in \{col_1\}}^m f_{1,i} \]
转移就是把每个儿子上面颜色不一样的求和乘起来
我们发现这个做法是 \(O(n^3)\) 的,并不能通过
然后我们优化一下这个转移,把每个儿子的方案求个和
对应乘的时候直接减掉那个颜色就成了
(挺水的,写博客是为了纪念自己做的第一个计数题……)
Code
#include
using namespace std;
namespace yspm{
inline int read()
{
int res=0,f=1; char k;
while(!isdigit(k=getchar())) if(k=='-') f=-1;
while(isdigit(k)) res=res*10+k-'0',k=getchar();
return res*f;
}
const int mod=1e9+7,N=5010;
int n,m,head[N],cnt;
struct node{
int to,nxt;
}e[N<<1];
inline void add(int u,int v)
{
e[++cnt].to=v; e[cnt].nxt=head[u];
return head[u]=cnt,void();
}
int sum[N],f[N][N];
inline void dfs(int x,int fa)
{
for(int i=head[x];i;i=e[i].nxt)
{
int t=e[i].to;
if(t==fa) continue; dfs(t,x);
for(int j=1;j<=m;++j)
{
f[x][j]=1ll*f[x][j]*((sum[t]-f[t][j]+mod)%mod)%mod;
}
}
for(int i=1;i<=m;++i) sum[x]=(sum[x]+f[x][i])%mod;
return ;
}
signed main()
{
n=read(); m=read();
for(int tmp,i=1;i<=n;++i)
{
tmp=read(); while(tmp--) f[i][read()]=1;
}
for(int i=1,u,v;i