tyvj 4877 组合数 唯一分解定理+前缀和优化

1.组合数
时间限制:1s
内存限制:256MB
【问题描述】
从m个不同元素中,任取n(n≤m)个元素并成一组,叫做从m个不同元素中取出n个元素的一个组合;从m个不同元素中取出n(n≤m)个元素的所有组合的个数,叫做从m个不同元素中取出n个元素的组合数,记作C(m,n)。
你的任务是:计算C(m,n)末尾有几个0。如C(10,1)=10,末位有一个0。
【输入】
输入文件名为zero.in。
第一行一个数T(<=1000),表示数据组数


对于每一组数据:输入两个数,m和n
【输出】
输出文件名为zero.out。
对于每组数据输出一行,包含一个数,表示C(m,n)末尾有几个0
【输入输出样例】
3
10 1
11 7
20 4
1
1
0
【数据说明】
对于30%的数据,1<=m<=20;
对于70%的数据,1<=m<=1000
对于100%的数据,1<=m<=1000000

题解:C(m,n)=m! / ( n! * (m-n)! ), 最终答案会出现0,仅有可能是2*5得到的,也就是说最多有多少对(2,5),用唯一分解定理分解后,将答案保存下来,维护前缀和,下次直接从now+1开始。

总结:这个题wa了一次超时两次,wa是因为把‘+’打成‘-’l,第一次Tle是因为朴素不加优化,求阶乘的2,5个数事重复算了,后来加了优化更慢,多了一个log,其实前缀和满足单调性,只需要记录now就行了。以后一定要细心些,善于优化。

	#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define N 1000000
using namespace std;
int T;
struct node
{
	long long re2,re5;
}m1,n1,mn,prime[N],cmt[N];
int date[N],cnt=0,now=0;
void query(int x,int y,node &opt)
{
	opt.re2=opt.re5=0;
	opt.re2+=cmt[x].re2;opt.re5+=cmt[x].re5;
	x++;
	for(int i=x;i<=y;i++)
	{
		int t=i;
	    while(t!=1 && !(t&1))	opt.re2++,t>>=1;
		while(t!=1 && !(t%5))   opt.re5++,t/=5;
		cmt[i].re2=opt.re2;cmt[i].re5=opt.re5;
	}
	now=max(now,y);
}
int main()
{
	int n,m;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&m,&n);
		if(m<=now)
		{
			m1.re2=cmt[m].re2;m1.re5=cmt[m].re5;
		}else query(now,m,m1);
		if(n<=now)
		{
		    n1.re2=cmt[n].re2;n1.re5=cmt[n].re5;
		}else query(now,n,n1);
		if(m<=now)
		{
			mn.re2=cmt[m-n].re2;mn.re5=cmt[m-n].re5;
		}else query(now,m-n,mn);
		long long a=m1.re2-(n1.re2+mn.re2),b=m1.re5-(n1.re5+mn.re5);
		printf("%lld\n",min(a,b));
		
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值