标签:整数 方案 return long pen 合数 name 插入 子序列
一个排列 \(A_n\) 被称为是对合的,当且仅当:
?1 ≤ i ≤ n, \(A_{Ai} = i\)
Cob 想知道长度为 \(n\) 的排列中,有多少个具有对合的性质。但他不幸
发现,这是个水题。因此,他把问题加强了一下,抛给了你:
给定一个 \(1\)~\(k\) 的排列 \(B_k\) (k ≤ n),试求出:有多少个长度为 \(n\) 的对合
排列,满足 \(B_k\) 是其子序列。
由于答案可能很大,请对 1004535809 (479 × 221 + 1) 取模后输出。
第一行两个整数 \(n\), \(k\)。
第二行 \(k\) 个整数,表示 \(B_k\)。
一行一个整数,表示答案
1 ≤ k ≤ n ≤ 1e4
首先找题目性质:
发现枚举\(B_i\)的前\(m\)个数在\(1\)~\(k\)匹配时
剩下\(k-m\)个数在k之外按顺序排好之后会一个找一个人回到原来的\(1\)~\(k\)匹配
那么前m个数需要按照顺序一个一个插入,所以方案是唯一的
那么后面的\(k-m\)个数可以通过组合数算方案数,剩下还有\(n\)-\(k\)个数可以发现就是没有任何限制的对合排列
直接递推预处理即可
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define R register
#define ll long long
using namespace std;
namespace IO
{
template<class T>
void rea(T &x)
{
char ch=getchar();int f(0);x = 0;
while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
x = f?-x:x;
}
template<class T>
T max(T a, T b) {return (a>b?a:b);}
template<class T>
T min(T a, T b) {return (a<b?a:b);}
}
using IO::rea;
#define mod 1004535809
long long n, k, b[10005], JieCheng[10005], inv[10005], ans, f[10005];
long long C(long long n, long long k){if(k > n) return 0;return JieCheng[n]*inv[k]%mod*inv[n-k]%mod;}
int kb[10005], is[10005];
bool vis[10005];
bool check(int m)
{
memset(vis, 0, sizeof vis);
for(R int i = 1; i <= m; ++i) kb[i] = b[i];
sort(kb+1, kb+1+m);
for(R int i = 1; i <= m; ++i) is[kb[i]] = i;
for(R int i = 1; i <= m; ++i) if(!vis[kb[i]])
{
int FG = i, lunhuan = 0;
vis[kb[i]] = 1;
while(!vis[b[FG]]) vis[b[FG]] = 1, FG = is[b[FG]], lunhuan++;
if(lunhuan >= 2) return 0;
}
return 1;
}
int main()
{
freopen("permu.in","r",stdin);
freopen("permu.out","w",stdout);
using IO::max;using IO::min;
rea(n), rea(k);
for(R int i = 1; i <= k; ++i) rea(b[i]);
JieCheng[1] = inv[1] = JieCheng[0] = inv[0] = 1;
f[1] = f[0] = 1;
for(R int i = 2; i <= max(n, k); ++i) JieCheng[i] = JieCheng[i-1]*i%mod;
for(R int i = 2; i <= max(n, k); ++i) inv[i] = inv[mod%i]*(mod-mod/i)%mod;
for(R int i = 2; i <= max(n, k); ++i) inv[i] = inv[i-1]*inv[i]%mod;
for(R int i = 2; i <= max(n, k); ++i) f[i] = (f[i-1]+(i-1)*f[i-2])%mod;
for(R int i = 0; i <= k; ++i)
{
long long X = 0, Y;
if(check(i)) X = 1;
Y = C(n-k, k-i);
ans = (ans+X*Y*f[n-2*k+i])%mod;
}
printf("%lld\n", ans);
return 0;
}
/*input
10 5
5 1 4 3 2
output
26
*/
标签:整数 方案 return long pen 合数 name 插入 子序列
原文地址:https://www.cnblogs.com/heanda/p/12404929.html