AtCoder Beginner Contest 066 D

来源:互联网 发布:centos和redhat的区别 编辑:程序博客网 时间:2024/06/06 19:01

题目链接: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 <bits/stdc++.h>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;}
原创粉丝点击