Tara's Beautiful Permutations 组合数学

https://www.hackerrank.com/contests/hourrank-15/challenges/taras-beautiful-permutations

 题意是说,给定一个数组,里面的数字最多出现两次,求所有的合法排列,合法排列定义为没有相同的数字排在一起。

 

首先先统计一下个数为2的数字的个数。

用all表示。

然后先不理题目要求,总排列数是A(n, n) / (2! * 2! * 2! .... * 2!),就是(2!)^all

下面蹦一波容斥。

暴力枚举i表示有i对东西是放在一起的,就是违反了规矩的,

然后这i对和身下的n - 2 * i个东西组合一起的情况有A(n - 2 * i + i) / (2!)^(all - i)种情况,容斥即可。

奇减偶加

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;


#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
const int MOD = 1e9 + 7;
LL quick_pow(LL a, LL b, LL MOD) {  //求解 a^b%MOD的值
    LL base = a % MOD;
    LL ans = 1; //相乘,所以这里是1
    while (b) {
        if (b & 1) {
            ans = (ans * base) % MOD; //如果这里是很大的数据,就要用quick_mul
        }
        base = (base * base) % MOD;    //notice。注意这里,每次的base是自己base倍
        b >>= 1;
    }
    return ans;
}

LL C(LL n, LL m, LL MOD) {
    if (n < m) return 0; //防止sb地在循环,在lucas的时候
    if (n == m) return 1;
    LL ans1 = 1;
    LL ans2 = 1;
    LL mx = max(n - m, m); //这个也是必要的。能约就约最大的那个
    LL mi = n - mx;
    for (int i = 1; i <= mi; ++i) {
        ans1 = ans1 * (mx + i) %MOD;
        ans2 = ans2 * i % MOD;
    }
    return (ans1 * quick_pow(ans2, MOD - 2, MOD) % MOD); //这里放到最后进行,不然会很慢
}
const int maxn = 2000 + 20;
int a[maxn];
map<int, int>book;
LL A(int n, int has, int MOD) {
    LL ans1 = 1;
    LL ans2 = 1;
    for (int i = 1; i <= n; ++i) {
        ans1 = ans1 * i % MOD;
    }
    for (int i = 1; i <= has; ++i) {
        ans2 = ans2 * 2 % MOD;
    }
    return (ans1 * quick_pow(ans2, MOD - 2, MOD) % MOD);
}
void work() {
    book.clear();
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        book[a[i]]++;
    }
    int all = 0;
    for (map<int, int> :: iterator it = book.begin(); it != book.end(); it++) {
        if (it->second == 2) {
            all++;
        }
    }
//    cout << all << endl;
    LL ans = A(n, all, MOD);
    if (all == 0) {
        cout << ans << endl;
        return;
    }
//    cout << ans << endl;
    for (int i = 1; i <= all; ++i) {
        if (i & 1) {
            ans = (ans + MOD - C(all, i, MOD) * A(n - i, all - i, MOD) % MOD) % MOD;
        } else {
            ans = (ans + C(all, i, MOD) * A(n - i, all - i, MOD) % MOD) % MOD;
        }
    }
    cout << ans << endl;
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    int t;
    scanf("%d", &t);
    while (t--) work();
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/liuweimingcprogram/p/6135008.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值