码迷,mamicode.com
首页 > 其他好文 > 详细

[Test1.11]-T3对合

时间:2020-03-03 22:41:22      阅读:66      评论:0      收藏:0      [点我收藏+]

标签:整数   方案   return   long   pen   合数   name   插入   子序列   

Description:

一个排列 \(A_n\) 被称为是对合的,当且仅当:
?1 ≤ i ≤ n, \(A_{Ai} = i\)
Cob 想知道长度为 \(n\) 的排列中,有多少个具有对合的性质。但他不幸
发现,这是个水题。因此,他把问题加强了一下,抛给了你:
给定一个 \(1\)~\(k\) 的排列 \(B_k\) (k ≤ n),试求出:有多少个长度为 \(n\) 的对合
排列,满足 \(B_k\) 是其子序列。
由于答案可能很大,请对 1004535809 (479 × 221 + 1) 取模后输出。

Input:

第一行两个整数 \(n\), \(k\)
第二行 \(k\) 个整数,表示 \(B_k\)

Output:

一行一个整数,表示答案

数据范围:

1 ≤ k ≤ n ≤ 1e4

Solution:

首先找题目性质:
发现枚举\(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
*/

[Test1.11]-T3对合

标签:整数   方案   return   long   pen   合数   name   插入   子序列   

原文地址:https://www.cnblogs.com/heanda/p/12404929.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!