链接
https://ac.nowcoder.com/acm/problem/15614
题解
可以发现,当我把两个相邻的数字捆绑在一起的时候,联通块少了一个,原先有 个联通快,假设有 个数字是「“重排后前方” 跟 “原排列前方” 一样的人」的,那么这样联通块个数就成了 。
那么接下来我想求出, 个联通块,重排之后,每个都不能和他原先相邻的那个联通块相邻,有多少种方案。不妨记作 。考虑从前往后依次加入每个联通块,那么第 个联通块不能放在第 个联通块的后面,这样分析,得到一个假的递推式: 。你会发现算出来的结果太小了,因为少算了 这种,因为 中不包含 这样的方案,所以自然插入第三个数字的时候也不会出现 这种方案。这个其实很好解决,我就是想算上那种第 个数字插入之后破坏了之前的相邻关系,并且最后没有相邻关系的,那么我就选两个数字让他们相邻。再把 这两个数字之间就好了,这部分方案的贡献是
于是
答案
代码
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#define iinf 0x3f3f3f3f
#define linf (1ll<<60)
#define eps 1e-8
#define maxn 1000010
#define cl(x) memset(x,0,sizeof(x))
#define rep(_,__) for(_=1;_<=(__);_++)
#define em(x) emplace(x)
#define emb(x) emplace_back(x)
#define emf(x) emplace_front(x)
#define fi first
#define se second
#define de(x) cerr<<#x<<" = "<<x<<endl;
#define mod 1000000007ll
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
ll read(ll x=0)
{
ll c, f(1);
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
for(;isdigit(c);c=getchar())x=x*10+c-0x30;
return f*x;
}
struct EasyMath
{
ll prime[maxn];
bool mark[maxn];
ll fastpow(ll a, ll b, ll c)
{
ll t(a%c), ans(1ll);
for(;b;b>>=1,t=t*t%c)if(b&1)ans=ans*t%c;
return ans;
}
void get_prime(ll N)
{
ll i, j;
for(i=2;i<=N;i++)mark[i]=false;
*prime=0;
for(i=2;i<=N;i++)
{
if(!mark[i])prime[++*prime]=i;
for(j=1;j<=*prime and i*prime[j]<=N;j++)
{
mark[i*prime[j]]=true;
if(i%prime[j]==0)break;
}
}
}
}em;
ll fact[maxn], _fact[maxn], n, f[maxn], ans;
ll C(ll n, ll m)
{
return fact[n]*_fact[m]%mod * _fact[n-m] %mod;
}
int main()
{
ll i, n=read(), k=read();
fact[0]=_fact[0]=1;
rep(i,n)fact[i]=fact[i-1]*i%mod, _fact[i]=em.fastpow(fact[i],mod-2,mod);
f[1]=f[2]=1;
for(i=3;i<=n;i++)f[i]=(f[i-2]*(i-2)+f[i-1]*(i-1))%mod;
for(i=0;i<=k;i++)(ans+=C(n-1,i)*f[n-i])%=mod;
printf("%lld",(ans+mod)%mod);
return 0;
}