[Jzoj] 3057. 电影票

题目大意

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

题目解析

假设将买《变形金刚3》票的人记为 1 1 1 。买《哈利波特7》的人记为 0 0 0,则 N N N个买《变形金刚3》的与 M M M个买《哈利波特7》的人的队伍就可以用一个具有 N N N 1 1 1 M M M 0 0 0的字符串,显然这样的字符串共有 C ( N + M , N ) C(N+M,N) C(N+M,N个。

其中不满足问题要求的串一定存在一个最靠左的位置 P P P,使得从第一个字符到第 P P P个字符为止的子串中 0 0 0的个数比 1 1 1的个数大 1 1 1.如 10100 10100 10100就是一个不满足条件的串,其中 P = 5 P=5 P=5

我们将从头到 P P P为止的子串中的字符 1 1 1换成 0 0 0则得到一个具有 N + 1 N+1 N+1 1 1 1 M − 1 M-1 M1 0 0 0的串,可以证明这种转换是一一对应的,即任意一个 N + 1 N+1 N+1 1 1 1 M − 1 M-1 M1 0 0 0的串都可以按照逆规则转换成一个不满足题目条件的串,转换规则为在任意一个 N + 1 N+1 N+1 1 1 1 M − 1 M-1 M1 0 0 0中找到最靠左的 P P P,使得从头到 P P P的子串中 1 1 1的个数比 0 0 0个数大 1 1 1,将到 P P P为止的子串中的 1 1 1 0 0 0互换则得到 N N N 1 1 1 M M M 0 0 0的串,且此串一定不能满足条件,而具有 N + 1 N+1 N+1 1 1 1 M − 1 M-1 M1 0 0 0的串只有 C ( N + M , N + 1 ) C(N+M,N+1) C(N+M,N+1

那么 a n s = C ( N + M , N ) − C ( N + M , N + 1 ) = ( N + 1 − M ) ∗ ( M + N ) ! / ( M ! ∗ ( N + 1 ) ! ) ans=C(N+M,N)-C(N+M,N+1)=(N+1-M)*(M+N)!/(M!*(N+1)!) ans=C(N+M,N)C(N+M,N+1)=(N+1M)(M+N)!/(M!(N+1)!)种方案可满足条件。

由于问题的规模很大,结果远远超出 l o n g i n t longint longint,要用高精度算法,虽然结果表达式中有分母,但实际结果一定是整数,所以不需要除法运算,具体运算时只要求出 ( N + 1 − M ) ∗ ( M + N ) ! / ( M ! ∗ ( N + 1 ) ! (N+1-M)*(M+N)!/(M!*(N+1)! (N+1M)(M+N)!/(M!(N+1)!的质因子分解式,然后做高精度压位即可。

代码

#include<bits/stdc++.h>
#define M 100000000
using namespace std;
int n,m;
int p[2005],b[2005];
bool vis[10005];
long long a[1005];
void mul(int x)
{
	for(int j=a[0];j>=1;j--)
	 a[j]*=x,a[j+1]+=a[j]/M,a[j]%=M;
	if(a[a[0]+1]) a[0]++;
}
void fun(int t,int k)
{
	int cnt=1;
	while(t!=1)
	{
	  if(t%p[cnt]==0) b[cnt]+=k,t/=p[cnt];
	  else cnt++;
	}
}
int main()
{
	for(int i=2;i<=10000;i++) if(!vis[i]) for(int j=i*2;j<=10000;j+=i) vis[j]=1;
	for(int i=2;i<=10000;i++) if(!vis[i]) p[++p[0]]=i;
	a[0]=a[1]=1;
	cin>>n>>m;
	int t,cnt;
	for(int i=2;i<=n+m;i++) fun(i,1);
	fun(n-m+1,1);
	for(int i=2;i<=m;i++) fun(i,-1);
	for(int i=2;i<=n+1;i++) fun(i,-1);
	for(int i=1;i<=p[0];i++)
	 for(int j=1;j<=b[i];j++)
	  mul(p[i]);
	cout<<a[a[0]];
	for(int i=a[0]-1;i>=1;i--)
	 printf("%08d",a[i]);
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值