AtCoder Beginner Contest 066 D - 11

题目链接:D - 11

题目大意:给你一个序列,这个序列有n+1个数,1到n这每个数至少出现一次,问这个序列长度为1-n+1的子序列分别有多少种,结果对1e9+7取模

题目思路:因为1-n每个数字至少出现一次,也就是说只有一个数重复出现了两次,那么我们首先可以考虑不重复的情况,直接就是C(n+1,k),n+1为序列种的总个数,k为当前选中的子序列的长度,这是不重复的个数,那么我们是需要减去重复个数的,比如3 4 5 6 7 1 2 5 8 9这个序列。
1、我们可以知道当两个五都出现的时候,这个序列一定不会被重复,这个是显而易见的,其它的数都不重复,随便选序列都不重复
2、同理,两个五都不出现也不会产生重复的数列。
3、那么就是出现一个五的情况的,(1)当出现一个五并且6 7 1 2任意出现一个或者多个的时候一定不会重复,因为6 7 1 2这四个数对两个五的相对位置是不同的,比如6 7 5和5 6 7这两个序列,虽然5只出现了一次,但是这两个序列明显不会重复(2)如果6 7 1 2这四个数都不出现呢,那么一定就是有两种情况,比如3 4 5这个序列,这个5可以是第一个5,也可以是第二个5,所以这就会产生重复了,所以我们只需要减掉这一部分就可以了,因为5用掉了一个数,那么这时候剩下了k-1个数,这k-1个数要从剩下的数里面选,而且这些数不能是两个重复数之间的数,所以只能选n+1-(d+1),d为两个数之间的距离,如这里为5,所以答案是4,所以最总我们得到的结果是(C(n+1,k)-C(n-d,k-1))%MOD,算组合数的时候用Luacs会T掉,我们可以看到n的数据范围为1e5,所以预处理加逆元算组合数就好了。

#include 

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 10, MOD = 1e9 + 7;
ll pos[maxn],fac[maxn],facm[maxn];

ll quick_pow(ll a,ll n,ll p)
{
    ll x = a;
    ll res = 1;
    while(n)
    {
        if(n & 1)
        {
            res = ((ll)res * (ll)x) % p;
        }
        n >>= 1;
        x = ((ll)x*(ll)x) % p;
    }
    return res;
}

ll C(ll n,ll k){
    if(k > n) return 0ll;
    ll ans = fac[k]*fac[n-k]%MOD;
    ans = (fac[n]*quick_pow(ans,MOD-2ll,MOD))%MOD;
    return ans;
}

int main()
{
    fac[0] = 1;
    for(int i = 1;i < maxn;i++)
        fac[i] = (fac[i-1]*i)%MOD;
    ll n;
    scanf("%lld", &n);
    n++;
    ll m, x;
    for(int i = 1;i <= n;i++)
    {
        scanf("%lld", &x);
        if (pos[x])
        {
            m = n - (i - pos[x] + 1);
            break;
        }
        pos[x] = i;
    }
    for(int i = 1;i <= n;i++)
        printf("%lld\n", (C(n, i) - C(m, i-1)+MOD) % MOD);
    return 0;
}

你可能感兴趣的:(Atcoder,Hihocoder,小技巧-预处理,前缀和,数论-逆元,数论-求组合数,二项式定理)