题意:
给你一棵树,你要对所有节点定一个顺序序列,形如 p 1 ⊕ 1 p 2 ⊕ 2 p 3 ⋯ p n − 1 ⊕ n − 1 p n p_1 \oplus_1 p_2 \oplus_2 p_3\cdots p_{n-1}\oplus_{n-1} p_n p1⊕1p2⊕2p3⋯pn−1⊕n−1pn,其中 ⊕ i \oplus_i ⊕i 为 = = = 或 < < <, p 1 ∼ n p_{1\sim n} p1∼n 为 1 ∼ n 1\sim n 1∼n 的一个排列, 要满足父亲节点严格小于儿子节点,求不同的序列个数,其中两个序列不同当且仅当存在两个点 a , b a,b a,b 在两个序列中的关系( < , = , > <,=,> <,=,>)不同。
n ≤ 100 n\leq 100 n≤100。
题解:
法一:
有点类似拓扑序的 DP?
注意由于一个连续等于段内的点的顺序是无关的,所以我们 DP 的时候只需要记录连续等于段的数量。
设 f i , j f_{i,j} fi,j 表示仅考虑 i i i 子树内的点的顺序序列,其中构成了 j j j 个连续等于段的方案数。
合并子树时枚举新的连续等于段个数转移即可。时间复杂度为 O ( n 3 ) O(n^3) O(n3),类似一个树上背包,只不过转移是 O ( n ) O(n) O(n) 而不是 O ( 1 ) O(1) O(1) 的。
法二:
考虑将问题转化:现在要求你将每个节点都染上一种颜色,要求父亲节点颜色大于(注意这里为了方便,我们改成了大于)儿子节点颜色,且出现的颜色必须连续,求染色方案数。
考虑枚举出现的颜色的最小值 M M M,现在转化为:给每个节点染上一种 [ 1 , M ] [1,M] [1,M] 中的颜色,要求出现的颜色个数恰好为 M M M,记方案数为 g M g_M gM。
考虑容斥,考虑求出
f
i
f_i
fi 表示给每个节点染上一种
[
1
,
i
]
[1,i]
[1,i] 中的一种颜色,没有其他限制的方案数。那么:
g
M
=
∑
i
=
0
M
(
M
i
)
(
−
1
)
M
−
i
f
i
g_M=\sum_{i=0}^M\binom{M}{i}(-1)^{M-i}f_i
gM=i=0∑M(iM)(−1)M−ifi
f
i
f_i
fi 可以容易用树形 DP 求出。时间复杂度
O
(
n
2
)
O(n^2)
O(n2)。
#include<bits/stdc++.h>
#define N 110
#define fi first
#define se second
#define pii pair<int,int>
#define mk(a,b) make_pair(a,b)
using namespace std;
namespace modular
{
const int mod=1000000007,inv2=(mod+1)/2;
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline void Add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
inline void Dec(int &x,int y){x=x-y<0?x-y+mod:x-y;}
inline void Mul(int &x,int y){x=1ll*x*y%mod;}
}using namespace modular;
inline int poww(int a,int b)
{
int ans=1;
while(b)
{
if(b&1) Mul(ans,a);
Mul(a,a);
b>>=1;
}
return ans;
}
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
int n,m,fa[N];
int cnt,head[N],nxt[N],to[N];
int f[N][N],s[N][N],g[N];
int fac[N],ifac[N];
bool vis[N],nrt[N];
int C(int n,int m)
{
return mul(mul(fac[n],ifac[m]),ifac[n-m]);
}
vector<pii>e;
int find(int x)
{
return x==fa[x]?x:(fa[x]=find(fa[x]));
}
void adde(int u,int v)
{
nrt[v]=1;
to[++cnt]=v;
nxt[cnt]=head[u];
head[u]=cnt;
}
void dfs(int u)
{
if(vis[u])
{
puts("0");
exit(0);
}
vis[u]=1;
for(int j=1;j<=n+1;j++) f[u][j]=1;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
dfs(v);
for(int j=1;j<=n+1;j++)
{
if(u!=n+1) Mul(f[u][j],s[v][j-1]);
else Mul(f[u][j],s[v][j]);
}
}
for(int j=1;j<=n+1;j++)
s[u][j]=add(s[u][j-1],f[u][j]);
}
int main()
{
// freopen("1.in","r",stdin);
// freopen("1_my.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++)
{
int u=read();
char c=getchar();
int v=read();
if(c=='<') e.push_back(mk(u,v));
else
{
int a=find(u),b=find(v);
if(a!=b) fa[a]=b;
}
}
for(auto ne:e) adde(find(ne.fi),find(ne.se));
for(int i=1;i<=n;i++)
if(fa[i]==i&&!nrt[i]) adde(n+1,i);
dfs(n+1);
for(int i=1;i<=n;i++)
{
if(fa[i]==i&&!vis[i])
{
puts("0");
return 0;
}
}
fac[0]=1;
for(int i=1;i<=n+1;i++) fac[i]=mul(fac[i-1],i);
ifac[n+1]=poww(fac[n+1],mod-2);
for(int i=n+1;i>=1;i--) ifac[i-1]=mul(ifac[i],i);
int ans=0;
for(int i=1;i<=n+1;i++)
{
for(int j=1;j<=i;j++)
{
if((i-j)&1) Dec(g[i],mul(C(i,j),s[n+1][j]));
else Add(g[i],mul(C(i,j),s[n+1][j]));
}
Add(ans,g[i]);
}
printf("%d\n",mul(ans,inv2));
return 0;
}