前言
记得几个月前自己曾经做过一道关于一张DAG求排列的问题,现如今遇到了的是一道关于树求排列的问题.
这一个问题看似简单实际上有一个细节那就是他可以取=。
这就使得复杂度升高了.
sol
首先,最简单的插孔原理。这一个非常简单,做这道题时,蒟蒻我现推式子。
为什么要解决这个问题呢,因为最基本的,在不考虑等于的情况下,n个数插入m个数的空隙中就相当于题目中的把两组不等式合起来.
怎么推?
设序列A,B
由于发现合并后的序列C中,原本的关系顺序不能改变,也就是A1仍然在A2前面,A2仍然在A3前面,所以A1如果插入了一个位置,那么A2只能插A1后面的位置.
这个时候暴力搞法就是插入Ai时枚举Ai-1在哪个位置。但不妨这样想,先不考虑位置变化的情况,也就是默认A2可以在A1前面,那么这样的插入情况就是:
原本有m个孔,然后插入一个以后,就会变成m+1个孔,因为会新增插入的那个数左右两边的孔,这样的情况是(m+1)×(m+2)×…..×(m+n).
然后把这样的情况除以一个n的排列,就能保证A1在A2前面了.
所以式子变成
如果我么给上下同时乘上一个 m! m ! 那么就会有
显然一个组合数.
换一种理解实际上就是合并后有m+n个数,这m+n个数中随意挑选n个位置作为f放A序列的,然后剩下的m个位置放B序列.
接着考虑有等号的情况。我们发现如果是一个序列里有等号,那么等号左右两边的相当于是一个数,然后就可以把他们缩在一起,这样等号就被消掉了。
我们如果想让两个没有等号的序列合在一起变成有等号的怎么办?
我们发现如果Ai要等与Bj,那么Ai的考虑可以插入的孔就会减少,因为他不能插在两个数中间,只能插在一个数上面,而且那个数只能允许他一个人插.
这样的理解实在没有什么好的进展。但我们知道如果枚举合并后有多少个等号就可以了。也就是说长度为n的A序列,m的B序列合并成p的C序列.
显然的,可以确定p > n, p > m,p < m + n;
然后,还是在p个位置中选出n个放A序列,这时,剩下的位置已经不足以放B序列,但是可以在已经放好A序列的位置里选出m+n-p个位置放B序列,所以说,方案数为
可以看出,开头提及的只是一种特殊情况。
code
#include<bits/stdc++.h>
using namespace std;
template <class T>
inline void read(T&data){
data=0;
register char ch=0;
while(ch<'0'||ch>'9')ch=getchar();
while(ch<='9'&&ch>='0'){
data=(data<<3)+(data<<1)+(ch^48);
ch=getchar();
}
return;
}
const int _ =101;
const int mod = 1e9+7;
int N,M;
int c[_<<1][_<<1],cnt,parent[_],to[_<<1],nxt[_],head[_],degree1[_],degree2[_],size[_],dp[_][_],g[_],Dp[_];
bool vis[_];
struct rel{
int fa,son;
}po[_];
inline int poww(register int x,register int y){
register int ret=1,base=x;
for(;y;y>>=1){
if(y&1)ret=1LL*ret*base%mod;
base=1LL*base*base%mod;
}
return ret;
}
inline int getni(register int x){return poww(x,mod-2);}
inline void init(){
c[0][0]=1;for(register int i=1;i<=2*N;++i)c[i][0]=1;
for(register int i=1;i<=2*N;++i){
for(register int j=1;j<=i;++j){
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
}
}
inline void add(register int a,register int b){to[++cnt]=b,nxt[cnt]=head[a],head[a]=cnt;}
inline int fi(register int x){return x==parent[x]?x:parent[x]=fi(parent[x]);}
void dfs(register int now){
//cout<<now<<endl;
vis[now]=1;
for(register int i=head[now];i;i=nxt[i]){
dfs(to[i]);
if(size[now]==0){
for(register int j=size[to[i]];j>=1;--j){
dp[now][j]=dp[to[i]][j];
}
size[now]+=size[to[i]];
continue;
}
for(register int j=size[now]+size[to[i]];j>=1;--j)g[j]=0;
for(register int j=size[to[i]];j>=1;--j){
if(dp[to[i]][j]==0)break;
for(register int o=size[now];o>=1;--o){
if(dp[now][o]==0)break;
for(register int w=j+o;w>=max(j,o);--w){
g[w]+=1LL*dp[now][o]*dp[to[i]][j]%mod*c[w][j]%mod*c[j][o-w+j]%mod;
g[w]%=mod;
}
}
}
size[now]+=size[to[i]];
for(register int j=size[now];j>=1;--j)dp[now][j]=g[j];
}
if(size[now]==0){size[now]=1;dp[now][1]=1;return;}
for(register int i=size[now];i>=0;--i)dp[now][i+1]=dp[now][i];size[now]++;
return;
}
int main(){
register int cc=0 ;
read(N),read(M);
init();
for(register int i=1;i<=N;++i)parent[i]=i;
for(register int i=1;i<=M;++i){
register int a,b;register char rr=0;
read(a);
while(rr!='='&&rr!='<')rr=getchar();
read(b);
if(rr=='='){
register int fa=fi(a),fb=fi(b);parent[fb]=fa;
}
else
po[++cc].fa=a,po[cc].son=b;
}
for(register int i=1;i<=cc;++i)
add(fi(po[i].fa),fi(po[i].son)),++degree1[fi(po[i].fa)],++degree2[fi(po[i].son)];
register int ans=0,Sz=0;
for(register int i=1;i<=N;++i){
if(vis[fi(i)]==0&°ree2[fi(i)]==0){
dfs(fi(i));
register int qio=fi(i);
if(Sz==0){
for(register int j=size[qio];j>=1;--j){
Dp[j]=dp[qio][j];
}
Sz+=size[qio];
continue;
}
for(register int j=Sz+size[qio];j>=1;--j)g[j]=0;
for(register int j=size[qio];j>=1;--j){
if(dp[qio][j]==0)break;
for(register int o=Sz;o>=1;--o){
if(Dp[o]==0)break;
for(register int w=j+o;w>=max(j,o);--w){
g[w]+=1LL*Dp[o]*dp[qio][j]%mod*c[w][j]%mod*c[j][o-w+j]%mod;
g[w]%=mod;
}
}
}
Sz+=size[qio];
for(register int j=Sz;j>=1;--j)
Dp[j]=g[j];
}
}
for(register int i=1;i<=N;++i){ans=ans+Dp[i];ans%=mod;}
printf("%d",ans);
}