题目大意
定义仙人球为一个无自环重边的、每个点最多属于一个简单环的无向连通图。
现在给定一个
n
个点
求出方案数模
1≤n≤5×103,1≤K≤min(n,100)
题目分析
可以发现仙人球就是一堆环形成一个树形结构(孤立的单点也可以看做环)。
那么我们考虑使用动态规划。
设
fi,j
表示做到第
i
个环,以这个环为根的子仙人球总共选择了
考虑将环上每个点为根的子仙人球的信息放到点上,令
hi,j
表示当前环第
i
个点为根的子仙人球(如下图颜色圈中的各个块)选择了
hi
可以很方便地由第
i
个点的所有儿子的
那么现在考虑知道了
hi
怎么求出
f
。
现在我们相当于在一个环上有若干个关键点,第
我们令
令
tot
为环上关键点个数。我们得出这个以后,就可以通过
来计算 f 的值,后面那个sigma显然可以使用后缀和维护,而为什么是从
那么知道了 f ,我们怎么统计答案呢?
设
怎么计算 g 呢?
我们令 Gi,j 表示环上第 i 个关键点向前连续选点,选了
至于第二种情况呢?可以发现它其实就是从第一个点和最后一个点分别向两边延伸,和 f 的计算过程是差不多的,唯一的不同就是从最后一个点延伸那边不能一个点都不选,不然就会算重。
计算过程中不需要显式地弄
所有过程都是 O(nK2) 的。
代码实现
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <stack>
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int P=1000000007;
const int N=5050;
const int M=N<<1;
const int E=M<<1;
const int MAXK=105;
int f[N][MAXK],g[N][MAXK],h[N][MAXK],F[2][N][MAXK],G[N][MAXK],sum[MAXK];
int last[N],bel[N],DFN[N],low[N];
int tov[E],rev[E],nxt[E];
stack<int> st;
int cir[N][N];
int idx,cnt,n,m,tot,K,ans,root;
void insert(int x,int y,int r){tov[++tot]=y,rev[tot]=tot+r,nxt[tot]=last[x],last[x]=tot;}
void tarjan(int x,int fe)
{
DFN[x]=low[x]=++idx,st.push(x);
for (int i=last[x],y;i;i=nxt[i])
if (i!=fe)
if (!DFN[y=tov[i]]) tarjan(y,rev[i]),low[x]=min(low[x],low[y]);
else low[x]=min(low[x],DFN[y]);
if (DFN[x]==low[x])
{
++cnt;
int y;
do{y=st.top(),cir[bel[y]=cnt][++cir[cnt][0]]=y,st.pop();}while (x!=y);
for (int i=1;i*2<=cir[cnt][0];i++) swap(cir[cnt][i],cir[cnt][cir[cnt][0]+1-i]);
if (x==1) root=cnt;
}
}
void dp(int cid,int fa)
{
int son=cir[cid][0];
for (int i,x,y,j=1;j<=son;j++)
for (i=last[x=cir[cid][j]];i;i=nxt[i])
if ((y=tov[i])!=fa&&bel[y]!=cid) dp(bel[y],x);
for (int i,x,y,u,j=1;j<=son;j++)
{
for (i=0;i<=K;i++) h[j][i]=0;
h[j][1]=1;
for (i=last[x=cir[cid][j]];i;i=nxt[i])
if ((y=tov[i])!=fa&&(u=bel[y])!=cid)
for (int k=K;k>=1;k--)
for (int l=1;l<=K-k;l++)
(h[j][k+l]+=1ll*h[j][k]*f[u][l]%P)%=P;
}
/*calculate G*/
for (int i=1;i<=K;i++) G[1][i]=h[1][i];
for (int i=2;i<=son;i++)
for (int j=K;j>=1;j--)
{
G[i][j]=h[i][j];
for (int k=1;k<=K-j;k++) (G[i][j+k]+=1ll*G[i-1][j]*h[i][k]%P)%=P;
}
for (int i=1;i<=son;i++)
for (int j=1;j<=K;j++)
(g[cid][j]+=G[i][j])%=P;
/*calculate F*/
for (int i=1;i<=K;i++) F[0][1][i]=h[1][i];
for (int i=2;i<=son;i++)
for (int j=K;j>=1;j--)
{
F[0][i][j]=0;
for (int k=1;k<=K-j;k++) (F[0][i][j+k]+=1ll*F[0][i-1][j]*h[i][k]%P)%=P;
}
for (int i=1;i<=K;i++) F[1][son][i]=h[son][i];
for (int i=son-1;i>=1;i--)
for (int j=K;j>=1;j--)
{
F[1][i][j]=0;
for (int k=1;k<=K-j;k++) (F[1][i][j+k]+=1ll*F[1][i+1][j]*h[i][k]%P)%=P;
}
for (int i=0;i<=K;i++) sum[i]=0;
for (int i=1;i<=K;i++) f[cid][i]=F[1][1][i];
sum[0]=1;
for (int i=son-1;i>=1;i--)
{
for (int j=1;j<=K;j++)
for (int k=0;k<=K-j;k++)
{
(f[cid][j+k]+=1ll*F[0][i][j]*sum[k]%P)%=P;
if (k) (g[cid][j+k]+=1ll*F[0][i][j]*sum[k]%P)%=P;
}
for (int j=1;j<=K;j++) (sum[j]+=F[1][i+1][j])%=P;
}
}
void calc()
{
ans=0;
for (int i=1;i<=cnt;i++)
for (int j=1;j<=K;j++)
(ans+=g[i][j])%=P;
}
int main()
{
freopen("cactus.in","r",stdin),freopen("cactus.out","w",stdout);
n=read(),m=read(),K=read();
for (int i=1,x,y;i<=m;i++) x=read(),y=read(),insert(x,y,1),insert(y,x,-1);
tarjan(1,0),dp(root,0),calc();
printf("%d\n",ans);
fclose(stdin),fclose(stdout);
return 0;
}