[SPOJ INS14G Kill them All]数学、排列组合

[SPOJ INS14G Kill them All]数学、排列组合

分类:排列组合

1. 题目链接

[SPOJ INS14G Kill them All]

2. 题意描述

n个恐怖分子,Digo和Sharry要把他们全杀了。
恐怖分子按固定的顺序站好。每个恐怖分子都可以被Digo或Sharry杀。
Digo想要确保每个时刻他杀的恐怖分子都比Sharry多,问有多少种这样的方案。
输入数据:
t, 测试样例数,
n,恐怖分子数,
1<=t<=100000,1<=n<=1000000
方案数mod1e9+7.

3. 解题思路

解法一:
我首先的想法是打表,找规律。然后也可以找出一个规律。即,

ans[i]={ans[i1]2ans[i1]2i2i;

但是需要注意的是 nlog(MOD) 的复杂度是会TLE的。我们需要 O(n)[1,n]
原理
desc.png
解法二:

放到笛卡尔坐标系中考虑,向上走表示我赢了,向下走表示我输了。相当于我从原点出发,我的轨迹不能在过程中碰到x轴。
第一步一定向上,转化为从原点出发,轨迹在过程中不能跑到x轴下面。
枚举向上步数m,总方案数为C(n,m)(n剪了1),现在我们要减去非法方案数。对于非法的轨迹,我们把它第一次碰到y=-1后的轨迹按y=-1翻转,终点是确定的(自己想一想为什么),并且翻转后的轨迹和原轨迹之间一一对应。
ok,终点在哪里呢? m - (n - m) + p = -2 -> p = -2 + n - 2m -> x - (n - x) = -2 + n - 2m -> x = n - m - 1
所以非法方案数为C(n,n-m-1)=C(n,m+1),向上步数为m的方案数为C(n,m)-C(n,m+1);
累加消掉中间项,答案为C(n,(n+1)/2)

可以O(n)预处理出阶乘的逆元。然后O(1)求出组合数。

4. 实现代码

/**
 * 解法一
 */
#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef long double LB;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;

const int INF = 0x3f3f3f3f;
const LL INFL = 0x3f3f3f3f3f3f3f3fLL;
const LB eps = 1e-8;
const int MAXN = 1000000 + 7;
const int MOD = 1e9 + 7;

int T, n;
LL ans[MAXN], inv[MAXN];
/*
void gen() {
    static LL dp[MAXN][MAXN];
    dp[1][1] = 1;
    for(int i = 2; i < MAXN; ++i) {
        LL sum = 0;
        dp[i][0] = 0;
        for(int j = 1; j <= i; ++j) {
            if((j << 1) <= i) dp[i][j] = 0;
            else dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % MOD;
            sum = (sum + dp[i][j]) % MOD;
        }
        if(i <= 100) printf("%d %lld\n", i, sum);
    }
}*/
template <typename T>
inline bool scan_d (T &ret) {
    char c;
    int sgn;
    if (c = getchar(), c == EOF) return 0; //EOF
    while (c != '-' && (c < '0' || c > '9') ) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return 1;
}
template<typename T>
void print(T x) {
    static char s[33], *s1; s1 = s;
    if (!x) *s1++ = '0';
    if (x < 0) putchar('-'), x = -x;
    while(x) *s1++ = (x % 10 + '0'), x /= 10;
    while(s1-- != s) putchar(*s1);
}
template<typename T>
void println(T x) {
    print(x); putchar('\n');
}

void init() {
    inv[1] = ans[1] = 1;
    // 预处理逆元
    for(int i = 2; i < MAXN; ++i) {
        inv[i] = (MOD -(MOD / i)) * inv[MOD % i] % MOD;
    }
    for(int i = 2; i < MAXN; ++i) {
        if(i & 1) {
            ans[i] = (ans[i - 1] << 1);
            while(ans[i] >= MOD) ans[i] -= MOD;
        } else {
            ans[i] = ans[i - 1] * (i - 1) % MOD * inv[i >> 1] % MOD;
        }
    }
}

int main() {
#ifdef ___LOCAL_WONZY___
    freopen("input.txt", "r", stdin);
#endif // ___LOCAL_WONZY___
    init();
    scan_d(T);
    while(T --) {
        scan_d(n);
        println(ans[n]);
    }
    return 0;
}
/**
 * 解法二
 */
#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef long double LB;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;

const int INF = 0x3f3f3f3f;
const LL INFL = 0x3f3f3f3f3f3f3f3fLL;
const LB eps = 1e-8;
const int MAXN = 1000000 + 7;
const int MOD = 1e9 + 7;

int T, n;
LL ans[MAXN], fac[MAXN], inv[MAXN];
/*
void gen() {
    static LL dp[MAXN][MAXN];
    dp[1][1] = 1;
    for(int i = 2; i < MAXN; ++i) {
        LL sum = 0;
        dp[i][0] = 0;
        for(int j = 1; j <= i; ++j) {
            if((j << 1) <= i) dp[i][j] = 0;
            else dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % MOD;
            sum = (sum + dp[i][j]) % MOD;
        }
        if(i <= 100) printf("%d %lld\n", i, sum);
    }
}*/
template <typename T>
inline bool scan_d (T &ret) {
    char c;
    int sgn;
    if (c = getchar(), c == EOF) return 0; //EOF
    while (c != '-' && (c < '0' || c > '9') ) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return 1;
}
template<typename T>
void print(T x) {
    static char s[33], *s1; s1 = s;
    if (!x) *s1++ = '0';
    if (x < 0) putchar('-'), x = -x;
    while(x) *s1++ = (x % 10 + '0'), x /= 10;
    while(s1-- != s) putchar(*s1);
}
template<typename T>
void println(T x) { print(x); putchar('\n');}
LL qpow(LL a, LL b) {
    LL ret = 1;
    while(b > 0) {
        if(b & 1) ret = ret * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return ret;
}

LL C(LL n, LL k) { return fac[n] * inv[k] % MOD * inv[n - k] % MOD; }

void init() {
    fac[0] = 1;
    for(int i = 1; i < MAXN; ++i) fac[i] = fac[i - 1] * i % MOD;
    inv[MAXN - 1] = qpow(fac[MAXN - 1], MOD - 2);
    for(int i = MAXN - 2; i >= 0; --i) inv[i] = inv[i + 1] * (i + 1) % MOD;

    for(int i = 0; i < MAXN; ++i) ans[i] = C(i - 1, i >> 1);
}

int main() {
#ifdef ___LOCAL_WONZY___
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w+", stdout); gen();
#endif // ___LOCAL_WONZY___
    init();
    scan_d(T); //scanf("%d", &T);
    while(T --) {
        scan_d(n); //scanf("%d", &n);
        println(ans[n]); //printf("%lld\n", ans[n]);
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值