【题目】
BZOJ
有 n n n个物品和 m m m个质量关系(小于或等于),每个物品至多有一个小于关系(即比某个物品质量小),求满足所有关系的质量序列数。 n ≤ 100 n\leq 100 n≤100
【解题思路】
题目给定的关系满足每个点至多有一条入边,则合法方案一定是一棵森林。
不妨将所有相等的点合起来,然后建立虚根跑树 DP \text{DP} DP。
令 f i , j f_{i,j} fi,j表示以 i i i为根的子树分成 j j j个不等段( j − 1 j-1 j−1个小于号)的方案,那么转移就是要将两个长分别为 a , b a,b a,b的序列合并。显然合并后长度 c c c,满足 max ( a , b ) ≤ c ≤ a + b \max(a,b)\leq c\leq a+b max(a,b)≤c≤a+b。同时有 f i , c = f v 1 , a ∗ f v 2 , b ∗ ( c a ) ∗ ( a b − ( c − a ) ) f_{i,c}=f_{v1,a}*f_{v2,b}*{c\choose a}*{a\choose b-(c-a)} fi,c=fv1,a∗fv2,b∗(ac)∗(b−(c−a)a)。
这个柿子的含义是先在 c c c个位置中选 a a a个,那么剩下 c − a c-a c−a个就放第二个序列中的数。接着从 b b b个数中选出 b − ( c − a ) b-(c-a) b−(c−a)个数字需要与 a a a中的数字进行合并。
复杂度 O ( n 3 ) O(n^3) O(n3),因为每一对点只会在 LCA \text{LCA} LCA处被 DP \text{DP} DP到。
【参考代码】
#include
#define mkp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=105,mod=1e9+7;
int n,m,tot,cnt,ans;
int head[N],fa[N],siz[N],du[N],C[N][N],f[N][N],g[N];
pii p[N];
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
struct Tway{int v,nex;}e[N<<1];
void add(int u,int v){e[++tot]=(Tway){v,head[u]};head[u]=tot;}
int findf(int x){return fa[x]==x?x:fa[x]=findf(fa[x]);}
void dfs(int x)
{
f[x][0]=1;
for(int i=head[x];i;i=e[i].nex)
{
int v=e[i].v;dfs(v);siz[x]+=siz[v];
for(int j=0;j<=siz[x];++j) for(int k=1;k<=siz[v];++k)
{
for(int l=max(j,k);l<=min(n,j+k);++l)
{
int now=1ll*C[l][j]*C[j][k-(l-j)]%mod;
now=1ll*now*f[x][j]%mod*f[v][k-1]%mod;
g[l]+=now;g[l]%=mod;
}
}
for(int j=0;j<=siz[x];++j) f[x][j]=g[j],g[j]=0;
}
++siz[x];
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ4013.in","r",stdin);
freopen("BZOJ4013.out","w",stdout);
#endif
n=read();m=read();
for(int i=0;i<=n;++i)
{
fa[i]=i;C[i][i]=C[i][0]=1;
for(int j=1;j<i;++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
for(int i=1;i<=m;++i)
{
int x,y;char ch[2];
x=read();scanf("%s",ch);y=read();
if(ch[0]=='=') fa[findf(x)]=findf(y);
else p[++cnt]=mkp(x,y);
}
for(int i=1;i<=cnt;++i) add(findf(p[i].fi),findf(p[i].se)),du[findf(p[i].se)]++;
cnt=0;
for(int i=1;i<=n;++i) if(findf(i)==i)
{
if(du[i]==0) add(n+1,i);
++cnt;
}
dfs(n+1);
for(int i=1;i<=n;++i) ans=(ans+f[n+1][i])%mod;
printf("%d\n",ans);
return 0;
}