电影票_纪中3057_组合数学

23 篇文章 0 订阅

Description


笨笨当了很久的道路调度员,笨笨也开始想体验生活,从生活中发现数学问题,锻炼自己思维。最近《变形金刚3》,《哈利波特7》同步放映,明显是决战雌雄,已知王府井中一共有n人买了《变形金刚3》的票,m人买了《哈利波特7》的票,并且n>=m,并且电影院中现在只有两种票,每次只有一个人买,(共有n+m次),这n+m次组成一个排列,为了保证每一个人买票时,《变形金刚3》票房都不少于《哈利波特7》,(n个买《变形金刚3》的人之间没区别,m个买《哈利波特7》的人也没区别),笨笨想着到这样的购票方案有多少种。笨笨想了好久都没想出来,所以笨笨找到了你。

Input


一行两个数n,m ( 0<=m<=n<=5000)

Output


输出方案种数

Hint


0<=m<=n<=5000

Analysis


类似的题目做过,购票问题

0分做法:

dfs爆搜可能性,优化剪枝一下可能拿10分+

0分做法:

经典的dp,设 f[i][j] 表示前 i 人有j人买《变形金刚3》则有
j2i f[i][j]+=f[i1][j] i 人买《哈利波特7》
f[i][j]+=f[i1][j1] i 人买《变形金刚3》
注意需要高精度,考虑到时间复杂度为O((n+m)n),再算上高精度时空都会爆,期望得分30,实际得分0(出题人的恶意)

正解:

假设将买《变形金刚3》票的人记为s,买《哈利波特7》的人记为x。
则n个买《变形金刚3》的与m个买《哈利波特7》的人的队伍就可以用一个具有n个s和m个x的字符串,显然这样的字符串共有C(n+m,n)个
其中不满足问题要求的串一定存在一个最靠左的位置p,使得从第一个字符到第p个字符为止的子串中x的个数比s的个数大1。
如sxsxx…就是一个不满足条件的串,其中p=5。
我们将从头到p为止的子串中的字符s换成x则得到一个具有n+1个s,m-1个x的串。
可以证明这种转换是一一对应的,即任意一个n+1个s,m-1个x的串都可以按照逆规则转换成一个不满足题目条件的串.
转换规则为在任意一个n+1个s,m-1个x中找到最靠左的p,使得从头到p的子串中s的个数比x个数大1,将到p为止的子串中的s与x互换则得到n个s和m个x的串.
且此串一定不能满足条件,而具有n+1个s,m-1个x的串只有C(n+m,n+1)

那么ans=C(n+m,n)-C(n+m,n+1)

本段文字转自某媳妇

计算的时候要高精度压位,c++大数打起来很累但用着爽

Code


#include <stdio.h>
#include <cstring>
#define mod 10000
#define maxn 1000
using namespace std;
struct num
{
    int s[maxn+1];
    __attribute__((optimize("O2")))
    inline  num operator *(int x)
    {
        num c={0};int v=0;
        for (int i=maxn;i>0;i--)
        {
            c.s[i]=s[i]*x+v;
            v=c.s[i]/mod;
            c.s[i]%=mod;
        }
        return c;
    }
    __attribute__((optimize("O2")))
    inline  num operator /(int x)
    {
        num c={0};int v=0;
        for (int i=1;i<=maxn;i++)
        {
            int t=v*mod+s[i];
            c.s[i]=t/x;
            v=t%x;
        }
        return c;
    }
    __attribute__((optimize("O2")))
    inline  num operator -(num b)
    {
        num c={0};int v=0;
        for (int i=maxn;i>0;i--)
        {
            if (s[i]-v>=b.s[i])
                c.s[i]=s[i]-b.s[i]-v,v=0;
            else
                c.s[i]=s[i]-b.s[i]-v+mod,v=1;
        }
        return c;
    }
    __attribute__((optimize("O2")))
    inline void output()
    {
        int i=1;
        while (!s[i])
            i++;
        printf("%d",s[i]);
        for (int j=i+1;j<=maxn;j++)
        {
            int p=4,f[5];
            memset(f,0,sizeof(f));
            do{
                f[p--]=s[j]%10;
            }while (s[j]/=10);
            for (int k=1;k<=4;k++)
                printf("%d",f[k]);
        }
        printf("\n");
    }
};
int main()
{
    int n,m;
    num a,b,ans;
    memset(a.s,0,sizeof(a.s));
    memset(b.s,0,sizeof(b.s));
    memset(ans.s,0,sizeof(ans.s));
    a.s[maxn]=b.s[maxn]=1;
    scanf("%d%d",&n,&m);
    int i,j;
    for (i=1,j=m+n;i<m;i++,j--)
        b=b*j,b=b/i;
    a=b*j,a=a/i;
    ans=a-b;
    ans.output();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值