题解:AT_yahoo_procon2019_final_e Espionage
zhangwenyuxuan · · 题解
可以把移动的方式转化为一张无向图,同一层之间是一个完全图,相邻的层之间对应点连边,我们要求以下东西的方案数:一个有向环,经过所有点,每个点的入度和出度都刚好为
由于只能在相邻的行之间移动,考虑每次加入一行,我们以下图为例:
把它从中间切开,会得到:
此时应该处理第四行,前三行已经填满,第四行的一些点与下面的点相连,方向确定,剩下的点还没有连边,我们称它们为孤立点。
容易发现,这些确定方向的点一定两两配对,在同一条链上。
我们要做的就是在当前行上加一些横向边,将不同的连通块合并,并且将一些纵向边延续下去。
由于最终只有一个环,并且所有点都在环上,所以我们不能把同一条链提前合成环,也不能在合并完当前行后还留下了孤立点。
如果合并两条链,会得到一条新的链,向下和向上的边都减少
如果先不考虑孤立点,将它们也看做链,合并是简单的。设
先将
但现在我们要求孤立点不能被留下来,考虑容斥,设
枚举
接下来以此加入一层求答案,设
但最后一行的转移有所不同,因为最终是要合并成一个环的,所以最终不能再留下任何链,直接把所有链和孤立点接成环即可:
但是
这样的时间复杂度是
:::info[参考代码]
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7,N=205;
int n,m,d,f[N],g[N];
struct matrix{
int a[N][N];
matrix operator *(matrix x)const{
matrix y;
for(int i=0;i<=d;i++){
for(int j=0;j<=d;j++){
y.a[i][j]=0;
for(int k=0;k<=d;k++){
y.a[i][j]=(y.a[i][j]+a[i][k]*x.a[k][j])%mod;
}
}
}
return y;
}
};
matrix getnew(){
matrix x;
for(int i=0;i<=d;i++){
for(int j=0;j<=d;j++){
x.a[i][j]=0;
}
x.a[i][i]=1;
}
return x;
}
int qpow1(int x,int y){
int sum=1;
while(y){
if(y&1){
sum=sum*x%mod;
}
x=x*x%mod;
y/=2;
}
return sum;
}
matrix qpow2(matrix x,int y){
matrix sum=getnew();
while(y){
if(y&1){
sum=sum*x;
}
x=x*x;
y/=2;
}
return sum;
}
int C(int x,int y){
return f[x]*g[x-y]%mod*g[y]%mod;
}
int S(int x,int y){
if(!x&&!y){
return 1;
}
if(!x||!y){
return 0;
}
return f[x]*g[y]%mod*C(x-1,y-1)%mod;
}
signed main(){
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(false);
cin>>n>>m;
d=n/2;
f[0]=1;
for(int i=1;i<=n;i++){
f[i]=f[i-1]*i%mod;
}
g[n]=qpow1(f[n],mod-2);
for(int i=n;i;i--){
g[i-1]=g[i]*i%mod;
}
matrix M;
for(int i=0;i<=d;i++){
for(int j=0;j<=d;j++){
M.a[i][j]=0;
for(int k=0;k<=min(n-2*i,j);k++){
M.a[i][j]=(M.a[i][j]+(k%2?-1:1)*C(n-2*i,k)*S(n-i-k,j-k))%mod;
}
}
}
matrix s;
for(int i=0;i<=d;i++){
for(int j=0;j<=d;j++){
s.a[i][j]=0;
}
}
s.a[0][0]=1;
matrix t=s*qpow2(M,m-1);
int ans=0;
for(int i=0;i<=d;i++){
ans=(ans+t.a[0][i]*f[n-i-1])%mod;
}
cout<<(ans+mod)%mod;
return 0;
}
:::