POJ 1150 The Last Non-zero Digit 数论+容斥

POJ 1150 The Last Non-zero Digit 数论+容斥

题目地址:

POJ 1150

题意:

求排列P(n, m)后面第一个非0的数。

分析:

为了熟悉题目中的理论,我先做了俩初级的题目

POJ 1401,题解见:POJ
1401 && ZOJ 2202 Factorial 阶乘N!的末尾零的个数

NYOJ 954,题解见:NYOJ
954 求N!二进制末尾几个0

这题想了一下,十进制末尾几个0可以转化为几个5因子,二进制最后一位非0可以转化为2因子,但是10进制就不行了,而且这个不是单单N!,而是n!/m!,orz。

实在太弱只能研究题解,推荐SCAU_Lyon巨巨的题解abilitytao巨巨的题解

然后我坐在电脑前面看了一晚上题解,终于,我发现我太弱了,我好不容易有点看懂了。

大概讲一下吧。

我们要把排列转化成前面两题的形式。

P(n, m) = n!/(n-m)!,我们让m = n - m直接按n!/m!做,也就是求m?(m+1)?(m+2)?....?n了。

这时候,我们只要统计在[m, n]这个范围里面的数的最后一位就行了,如果直接去暴力会跪,因为我们不仅要统计每个数的最后一位,还有一些2和5因子混在数中,我们还要消掉那些因子,然后取末位。

于是要把2和5抽出来,然后就只剩3,7,9,统计抽完后的3,7,9,然后还得记得:由于这里面2可能会比5多,所以要把多出来的2算出来。

只要知道如何统计n!,就能统计排列。

个中计算有很厉害的技巧,表示orz。

一、计算n!中消除2,5后末位为x的公式为: 

f(n, x) = n/10 + (n%10>=x) + f(n/2, x) + f(n/2, 5) - f(n/10, x).

解释下:

1. x限定3,7,9。

2. n/10 + (n%10>=x)也就是:每十个数以内末数字是3,7,9在没有除去2和5两种因子前都只会出现一次。

3. 加上抽出2和5后的子问题。(这里跟前面两题原理一样)

4. 抽出2和5的时候,会多抽出了一次10,这时候就要用容斥定理减去。

二、然后计算多余的2就比较容易理解了,就跟前面俩题一样,求出2的个数和5的个数,减一下就是n!中多出来的2的个数了。

三、然后我们就能得到[m,n]中抽出2,5后末位为3,7,9的个数,以及多出来的2的个数了。 

这时候如果直接while(cnt--)去算可能会超时+爆范围。

我们可以发现2^n,3^n,7^n,9^n的末位都是有规律可寻的。于是就能直接算了。

详细细节见代码。

代码:

/*
*  Author:      illuz <iilluzen[at]gmail.com>
*  File:        1150.cpp
*  Create Date: 2014-05-26 22:28:45
*  Descripton:
*/

#include <cstdio>
#include <cstring>

const int N = 2e5;

int cnt3, cnt7, cnt9, cnt2;
int n, m;
int rec[10][N];

// 计算n!中消除2,5后末位为x的数量
int f(int n, int k) {
	if (n < 1)
		return 0;
	if (n < N && rec[k][n] != -1)
		return rec[k][n];
	int ret = n / 10 + (n % 10 >= k) + f(n / 2, k) + f(n / 5, k) - f(n / 10, k);
	if (n < N)
		rec[k][n] = ret;
	return ret;
}

// 多出来的2的个数
int more(int n) {
	int num2 = 0, num5 = 0;
	int t = n;
	while (t != 0) {
		num2 += t / 2;
		t /= 2;
	}
	while (n != 0) {
		num5 += n / 5;
		n /= 5;
	}
	return num2 - num5;
}

int main()
{
	memset(rec, -1, sizeof(rec));
	while (~scanf("%d%d", &n, &m)) {
		if (m == 0) {
			puts("1");
			continue;
		}
		m = n - m;
		cnt2 = more(n) - more(m);
		cnt3 = f(n, 3) - f(m, 3);
		cnt7 = f(n, 7) - f(m, 7);
		cnt9 = f(n, 9) - f(m, 9);
		// printf("%d %d %d %d\n", cnt2, cnt3, cnt7, cnt9);

		// 2 4 8 6
		if (cnt2-- == 0)
			cnt2 = 1;
		else if (cnt2 % 4 == 0)
			cnt2 = 2;
		else if (cnt2 % 4 == 1)
			cnt2 = 4;
		else if (cnt2 % 4 == 2)
			cnt2 = 8;
		else
			cnt2 = 6;

		// 1 3 9 7
		if (cnt3 % 4 == 0)
			cnt3 = 1;
		else if (cnt3 % 4 == 1)
			cnt3 = 3;
		else if (cnt3 % 4 == 2) cnt3 = 9;
		else
			cnt3 = 7;

		// 1 7 9 3
		if (cnt7 % 4 == 0)
			cnt7 = 1;
		else if (cnt7 % 4 == 1)
			cnt7 = 7;
		else if (cnt7 % 4 == 2)
			cnt7 = 9;
		else
			cnt7 = 3;

		// 1 9
		if (cnt9 % 2 == 0)
			cnt9 = 1;
		else
			cnt9 = 9;

		printf("%d\n", cnt2 * cnt3 * cnt7 * cnt9 % 10);
	}
	return 0;
}

POJ 1150 The Last Non-zero Digit 数论+容斥

时间: 2025-01-31 09:11:44

POJ 1150 The Last Non-zero Digit 数论+容斥的相关文章

POJ 3695 Rectangles (矩形并 状压+容斥定理 好题)

Rectangles Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 3730   Accepted: 1082 Description You are developing a software for painting rectangles on the screen. The software supports drawing several rectangles and filling some of them w

Coprime (单色三角形+莫比乌斯反演(数论容斥))

这道题,先说一下单色三角形吧,推荐一篇noip的论文<国家集训队2003论文集许智磊> 链接:https://wenku.baidu.com/view/e87725c52cc58bd63186bd1b.html?from=search 单色三角形指的是n个顶点,有n(n-1)条边,很明显是每个点两两相连,那么这样所形成的所有三角形的边假如有两种颜色:红和黑.那么问一共有多少三角形的三边是一种颜色的个数. ,建议看一下那个论文,因为我只能直接给出你结论.  下面的数学符号:{...}为概率论中表

数论 + 容斥 - HDU 4059 The Boss on Mars

The Boss on Mars Problem's Link Mean: 给定一个整数n,求1~n中所有与n互质的数的四次方的和.(1<=n<=1e8) analyse: 看似简单,倘若自己手动推公式的话,还是需要一定的数学基础. 总的思路:先求出sum1=(1^4)+(2^4)+...(n^4),再求出sum2=(1~n中与n不互质的数的四次方的和),answer=sum1-sum2. 如何求sum1呢? 有两种方法: 1.数列差分.由于A={Sn}={a1^4+a2^4+...an^4}

hdu1695:数论+容斥

题目大意: 求x属于[1,b]和 y属于[1,d]的 gcd(x,y)=k 的方案数 题解: 观察发现 gcd()=k 不好处理,想到将x=x/k,y=y/k 后 gcd(x,y)=1.. 即问题转化为求区间 [1,b/k]和 [1,d/k]的互质数对个数 由于题目规定 (x,y)和(y,x)是同一种,所以我们可以规定 x<y,,然后只需对每一个y求出比他小的即可 公共部分可以通过欧拉函数快速求出.. 非公共部分就不行了.. 所以就分解质因数,用容斥的方法求了 #include <iostre

【bzoj2393】Cirno的完美算数教室 数论容斥

Description ~Cirno发现了一种baka数,这种数呢~只含有2和⑨两种数字~~ 现在Cirno想知道~一个区间中~~有多少个数能被baka数整除~ 但是Cirno这么天才的妖精才不屑去数啦 只能依靠聪明的你咯. Input 一行正整数L R ( 1 < L < R < 10^10) Output 一个正整数,代表所求的答案 Sample Input 1 100 Sample Output 58 HINT 此题数据范围应该是10^9 题解: 处理处所有数然后容斥. 1 #in

#数论-模运算#POJ 1150、1284、2115

1.POJ 1150 The Last Non-zero Digit #质因数分解+模运算分治# 先贴两份题解: http://www.hankcs.com/program/algorithm/poj-1150-the-last-non-zero-digit.html http://www.cppblog.com/abilitytao/archive/2009/10/31/99907.html 下面是自己看完题解(划掉)之后的理解: 题目要求出组合数Anm=n!/(n-m)!(说实话一开始不知道

poj 1150 The Last Non-zero Digit

1 /** 2 大意: 求A(n,m)的结果中从左到右第一个非零数 3 思路: 0是由2*5的得到的,所以将n!中的2,5约掉可得(2的数目比5多,最后再考虑进去即可) 4 那n!中2 的个数怎么求呢? 5 int get2(int n){ 6 if(n==0) 7 return 0; 8 return n/2+get2(n/2); 9 } 10 eg: 1*2*3*4*5*6*7*8*9*10 约去2,5可得,,1*1*3*1*1*3*7*1*9*1 11 所以最后肯定是3,7,9..的数列,

10162 - Last Digit (数论+周期规律)

UVA 10162 - Last Digit 题目链接 题意:求S=(11+22+...NN)%10 思路:打出0-9的每个周期,发现周期为1或2或4.所以S是以20一个周期,打出表后发现20为4,所以对应的40为8,60为2,80为6,100为0,100为1个周期,且为0,所以先把数字mod上100,然后在mod 20求出对应位置. 代码: #include <stdio.h> #include <string.h> const int Z2[10] = {0, 4, 8, 2

uva 10162 - Last Digit(数论)

题目链接:uva 10162 - Last Digit 题目大意:给定n,求s的个位的数值是多少. 解题思路:对于ii,重复周期为20,这样就有 1 4 7 6 5 6 3 6 9 0 1 6 3 6 5 6 7 4 9 0 但是这个周期的值是不为0的,总的话是100为一个大周期. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxt =