Luogu3914 染色计数

是道不错的题目

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

你可能感兴趣的:(Luogu3914 染色计数)