【JZOJ3057】【NOIP2012模拟10.26】电影票【数论】

题目大意:

题目链接:https://jzoj.net/senior/#main/show/3057
n n n个人买电影一的票, m m m个人买电影二的票。每次只有一个人买票,而且保证每时每刻电影一的票房都不小于电影二的票房。求买票顺序的方案数。


思路:

假设买电影一的票的人为 a a a,买电影儿的票的人为 b b b,那么买票顺序就构成了一个序列。例如 a a b b b , a a b a b b b a b , a b a b a aabbb,aababbbab,ababa aabbb,aababbbab,ababa。那么一个序列中有 n n n a a a m m m b b b就相当于在一个长度为 n + m n+m n+m的字符串里,选择其中 n n n个成为 a a a,剩下的都是 b b b
那么总的方案数就转化了 n + m n+m n+m个数中选择 n n n个数的方案数。自然就是 C n + m n C^n_{n+m} Cn+mn了。
但是其中还要去除其中某个时刻电影二的票房大于电影一的票房的情况。也就是说,对于一个 a b ab ab串,当某一个时刻 p p p(也就是该串的前 p p p个字符)的时候, s [ 1 ∼ p ] s[1\sim p] s[1p] b b b的个数大于 a a a的个数,那么该情况就是要舍去的。
如果该串有多个时刻 p p p满足上述条件,那么取最前面的为 p p p
容易得出,此时 b b b的个数一定比 a a a的个数多一。那么如果把 s [ 1 ∼ p ] s[1\sim p] s[1p]中的 a a a b b b取反( a a a b b b b b b a a a),那么由于 b b b的个数比 a a a的个数多一,所以去反后就会出现 n + 1 n+1 n+1 a a a m − 1 m-1 m1 b b b
那么只需找到有多少个 a b ab ab串含 n + 1 n+1 n+1 a a a m − 1 m-1 m1 b b b就可以了。因为取反后就会有 n n n a a a m m m b b b
那么求有多少个需要排除的答案就转换为 ( n + 1 ) + ( m − 1 ) = n + m (n+1)+(m-1)=n+m (n+1)+(m1)=n+m个数字中选择 n + 1 n+1 n+1个的方案数,即 C n + m n + 1 C^{n+1}_{n+m} Cn+mn+1
那么最终答案就是总方案数 − - 需排除方案数,即
C n + m n − C n + 1 n − m C^{n}_{n+m}-C{n+1}_{n-m} Cn+mnCn+1nm
利用通项公式得
( n + m ) ! n ! [ ( n + m ) − n ] ! − ( n + m ) ! ( n + 1 ) ! [ ( n + 1 ) + ( n − m ) ] ! \frac{(n+m)!}{n![(n+m)-n]!}-\frac{(n+m)!}{(n+1)![(n+1)+(n-m)]!} n![(n+m)n]!(n+m)!(n+1)![(n+1)+(nm)]!(n+m)!
简化得
( n + m ) ! n ! m ! − ( n + m ) ! ( n + 1 ) ! ( m − 1 ) ! \frac{(n+m)!}{n!m!}-\frac{(n+m)!}{(n+1)!(m-1)!} n!m!(n+m)!(n+1)!(m1)!(n+m)!
将分母中的 n ! n! n! ( n + 1 ) ! (n+1)! (n+1)!约分得
( n + 1 ) ( n + 2 ) . . . ( n + m ) m ! − ( n + 2 ) ( n + 3 ) . . . ( n + m ) ( m − 1 ) ! \frac{(n+1)(n+2)...(n+m)}{m!}-\frac{(n+2)(n+3)...(n+m)}{(m-1)!} m!(n+1)(n+2)...(n+m)(m1)!(n+2)(n+3)...(n+m)
T = ( n + 2 ) ( n + 3 ) . . . ( n + m ) T=(n+2)(n+3)...(n+m) T=(n+2)(n+3)...(n+m)
T ( n + 1 ) m ! − T ( m − 1 ) ! \frac{T(n+1)}{m!}-\frac{T}{(m-1)!} m!T(n+1)(m1)!T
将第二个分式上下同时乘 m m m
T ( n + 1 ) m ! − T m m ! \frac{T(n+1)}{m!}-\frac{Tm}{m!} m!T(n+1)m!Tm

T ( n + 1 − m ) m ! \frac{T(n+1-m)}{m!} m!T(n+1m)
我们要求的就是这个鬼东西了。
那么就可以暴力搞了。不需要像题解中分解质因数什么的。
注意的是,该题需要压位高精,压 8 8 8位后分子 T ( n + 1 − m ) T(n+1-m) T(n+1m)还是可能会很大,需要长度 3000 3000 3000的数组。而且在运算过程中,分子最高会达到 1 0 8 × 1 0 8 10^8\times 10^8 108×108,还是必须开 l o n g   l o n g long\ long long long
求出分子后,可以依次除以 1 , 2 , 3... m − 1 , m 1,2,3...m-1,m 1,2,3...m1,m,依旧可以达到直接除以 m ! m! m的效果。
时间复杂度 O ( m × M A X N ) O(m\times MAXN) O(m×MAXN),其中 M A X N MAXN MAXN表示数组长度(3000),所以还是不够优秀,但是可以省去很多恶心的操作。


代码:

#include <cstdio>
using namespace std;
typedef long long ll;

const int MAXN=3000;
int n,m;
ll s,a[MAXN+1],t;

int main()
{
    scanf("%d%d",&n,&m);
    a[MAXN]=1;
    for (int i=n+2;i<=n+m;i++)  //求T
        for (int j=MAXN;j>=1;j--)
        {
            a[j]=a[j]*(ll)i+t;
            t=a[j]/100000000;
            a[j]%=100000000;
        }
    for (int i=MAXN;i>=1;i--)  //乘上(n-m+1)
    {
        a[i]=a[i]*(ll)(n-m+1)+t;
        t=a[i]/100000000;
        a[i]%=100000000;
    }
    for (int i=1;i<=m;i++)  //依次除
    {
        s=0;
        for (int j=1;j<=MAXN;j++)
        {
            s=s*100000000+a[j];
            a[j]=s/(ll)i;
            s%=(ll)i;
        }
    }
    int i=0;
    while (!a[i]) i++;
    printf("%lld",a[i]);
    i++;
    for (;i<=MAXN;i++)
    {
        if (a[i]<10) printf("0000000");
        else if (a[i]<100) printf("000000");
        else if (a[i]<1000) printf("00000");
        else if (a[i]<10000) printf("0000");
        else if (a[i]<100000) printf("000");
        else if (a[i]<1000000) printf("00");
        else if (a[i]<10000000) printf("0");
        printf("%lld",a[i]);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值