【Codeforces】 CF512D Fox And Travelling

题目描述

Codeforces方向
Luogu方向

题目解法

首先可以用一个类似拓扑排序的方法(把度数为 0 0 0 加入变为度数小于 2 2 2 就加入)把所有可能被选择的点找出来
其中不可能选择的点不一定只有环上的点,也可能是类如连了多个不同的环的点

可以发现,对于可以被选择的点一定构成了多棵树
为什么不可能有多于 1 1 1 个点于环上的点相邻?因为这几个点之间的路径就永远不可能被删
所以可以把树分为 2 2 2 种:

  1. 有且仅有 1 1 1 个点与环上的点相邻
    可以指定与环上相邻的点为根,然后做一遍树形 d p dp dp 就可以了
    里面合并子树是背包,然后考虑子树的根节点一定在子树中最后被删除
  2. 没有点与环上的点相邻
    这就说明每个点都可以成为根节点
    不妨对每个点做一遍上述的 d p dp dp
    考虑选择 i i i 个点会被计算几次(若这棵树有siz个点)
    i ∈ [ 0 , s i z ) i\in [0,siz) i[0,siz) 时,因为上述 d p dp dp 中根一定是最后被选,所以当前情况会被 s i z − i siz-i sizi 个根计算,所以需要 / ( s i z − i ) /(siz-i) /(sizi)
    i = s i z i=siz i=siz 时,可以发现每个点为根恰好对应了当前点最后被选的情况,所以没有被多计算

时间复杂度为 O ( n 3 ) O(n^3) O(n3)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N(110),M(10100),P(1e9+9);
int n,m,rt,cnt;
int deg[N],ans[N],C[N][N];
int f[N][N],t[N],siz[N],Tt[N];
bool ok[N],vis[N];
int e[M],h[N],ne[M],idx;
int hh,tt=-1,que[N];
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
void dfs1(int u){
	vis[u]=1,cnt++;
	for(int i=h[u];~i;i=ne[i]) if(!ok[e[i]]) rt=u;
	for(int i=h[u];~i;i=ne[i]) if(!vis[e[i]]&&ok[e[i]]) dfs1(e[i]);
}
void dfs2(int u,int fa){
	memset(f[u],0,sizeof(f[u]));
	f[u][0]=1,siz[u]=0;
	for(int i=h[u];~i;i=ne[i]){
		int v=e[i];
		if(!ok[v]||v==fa) continue;
		dfs2(v,u);
		for(int j=0;j<=siz[u]+siz[v];j++) t[j]=0;
		for(int j=0;j<=siz[u];j++) for(int k=0;k<=siz[v];k++) (t[j+k]+=(LL)f[u][j]*f[v][k]%P*C[j+k][j]%P)%=P;
		siz[u]+=siz[v];
		for(int j=0;j<=siz[u];j++) f[u][j]=t[j];
	}
	siz[u]++,f[u][siz[u]]=f[u][siz[u]-1];
} 
void dfs3(int u,int fa){
	dfs2(u,0);
	for(int i=0;i<=n;i++) (Tt[i]+=f[u][i])%=P;
	for(int i=h[u];~i;i=ne[i]) if(e[i]!=fa&&ok[e[i]]) dfs3(e[i],u);
}
void merge(int rt,int *ad){
	for(int j=1;j<=n;j++) t[j]=0;
	for(int j=1;j<=n;j++) for(int k=0;k<=j;k++) (t[j]+=(LL)ans[k]*ad[j-k]%P*C[j][k]%P)%=P;
	for(int j=1;j<=n;j++) ans[j]=t[j];
}
void add(int a,int b){ e[idx]=b,ne[idx]=h[a],h[a]=idx++;}
int qmi(int a,int b,int p){
	int res=1;
	for(;b;b>>=1){
		if(b&1) res=(LL)res*a%p;
		a=(LL)a*a%p;
	}
	return res;
}
int main(){
	C[0][0]=1;
	for(int i=1;i<N;i++) for(int j=0;j<=i;j++) C[i][j]=(!j||i==j)?1:(C[i-1][j]+C[i-1][j-1])%P;
	n=read(),m=read();
	memset(h,-1,sizeof(h));
	for(int i=1,x,y;i<=m;i++){
		x=read(),y=read();deg[x]++,deg[y]++; 
		add(x,y),add(y,x);
	} 
	for(int i=1;i<=n;i++) if(deg[i]<2) que[++tt]=i;
	while(hh<=tt){
		int u=que[hh++];ok[u]=1;
		for(int i=h[u];~i;i=ne[i]){
			int v=e[i];deg[v]--;
			if(!ok[v]&&deg[v]<2) que[++tt]=v;
		}
	}
	ans[0]=1;
	for(int i=1;i<=n;i++)
		if(ok[i]&&!vis[i]){
			rt=0,cnt=0,dfs1(i);
			if(rt) dfs2(rt,0),merge(rt,f[rt]);//有根树 
			else{//无根树 
				memset(Tt,0,sizeof(Tt));
				dfs3(i,0);
				for(int j=0;j<cnt;j++) Tt[j]=(LL)Tt[j]*qmi(cnt-j,P-2,P)%P;
				merge(rt,Tt);
			}
		}
	for(int i=0;i<=n;i++) printf("%d\n",ans[i]);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值