逆元

目录

一,逆元

二,OJ实战

HYSBZ 1965 SHUFFLE 洗牌

CSU 1069 Changlong

CSU 1810 Reverse

HDU 5698 瞬间移动


一,逆元

费马小定理、逆元 数论_nameofcsdn的博客-CSDN博客

用费马小定理+快速幂可以求逆元。

二,OJ实战

HYSBZ 1965 SHUFFLE 洗牌

题目:

为了表彰小联为Samuel星球的探险所做出的贡献,小联被邀请参加Samuel星球近距离载人探险活动。 由于Samuel星球相当遥远,科学家们要在飞船中度过相当长的一段时间,小联提议用扑克牌打发长途旅行中的无聊时间。玩了几局之后,大家觉得单纯玩扑克牌对于像他们这样的高智商人才来说太简单了。有人提出了扑克牌的一种新的玩法。 对于扑克牌的一次洗牌是这样定义的,将一叠N(N为偶数)张扑克牌平均分成上下两叠,取下面一叠的第一张作为新的一叠的第一张,然后取上面一叠的第一张作为新的一叠的第二张,再取下面一叠的第二张作为新的一叠的第三张……如此交替直到所有的牌取完。 如果对一叠6张的扑克牌1 2 3 4 5 6,进行一次洗牌的过程如下图所示:

  

从图中可以看出经过一次洗牌,序列1 2 3 4 5 6变为4 1 5 2 6 3。当然,再对得到的序列进行一次洗牌,又会变为2 4 6 1 3 5。 游戏是这样的,如果给定长度为N的一叠扑克牌,并且牌面大小从1开始连续增加到N(不考虑花色),对这样的一叠扑克牌,进行M次洗牌。最先说出经过洗牌后的扑克牌序列中第L张扑克牌的牌面大小是多少的科学家得胜。小联想赢取游戏的胜利,你能帮助他吗?

Input

有三个用空格间隔的整数,分别表示N,M,L (其中0< N ≤ 10 ^ 10 ,0 ≤ M ≤ 10^ 10,且N为偶数)。

Output

单行输出指定的扑克牌的牌面大小。

Sample Input

6 2 3

Sample Output

6

解析:

编号为i(1<=i<=N)的牌,m次洗牌之后编号为i*2^m (mod N+1)

所以m次洗牌之后编号为L的牌,在开始洗牌之前的编号为L*t^m (mod N+1)

其中t是2的逆元,等于N/2+1

代码:

#include<iostream>
using namespace std;
 
long long N, M, L, t;
 
long long f(long long m)
{
	if (m == 0)return 1;
	long long ans = f(m / 2);
	ans = ans*ans % (N + 1);
	if (m % 2)ans *= t;
	return ans % (N + 1);
}
 
int main()
{
	cin >> N >> M >> L;
	t = N / 2 + 1;
	cout << f(M)*L % (N + 1) << endl;
	return 0;
}

CSU 1069 Changlong

题目:

Description
Changlong is a handsome and clever boy and he is admired by many beautiful girls. Every day he received tens of thousands of letters form girls which express their intense desire to be his life companion. You see, Changlong is very tired of this and one day he get an idea to avoid this bother. He declares:

Only those clever girls who can solve the problem below have the qualification to be his wife.

Changlong's problem can be formulated like this:

There is one interesting sequence of numbers a1,a2,...an, this sequence has a very attractive property that every number appears exactly p times except a particular number t, which occurs q times. How to extract this particular number t from this sequence? In order to let this problem more mysterious, Changlong assumes that the involved number p, q in his problem are prime to each other, i.e., gcd(p, q)=1.

Changlong's method works very well. After his declaration, the number of received letter has decrease sufficiently. The girls are beginning to think over the problem with continuous strenuous effort, no sooner later they find this problem is beyond their ability. So most of them give up, but there is still a beautiful girl who is persistent in this problem. One day this girl comes across you and she invites you to help her.

Input
At the first line of the input is a integer k(0<k<=100), the number of test cases.
For each test case, there will be a line containing three integers: n(0<n<=10^7), p, q(1<p, q<200, gcd(p,q)=1) and a line containing n elements ai(0<ai<10^7) of the sequence.
Output
  For each test case, you should first output a line with“Case #:”,where # is the id of the test case, then you should output the particular number t, i.e., the number which occurs q times in the sequence.

Sample Input
2
8 3 2
1 2 1 2 1 2 3 3
7 4 3
1 1 1 1 2 2 2
Sample Output
Case 1:
3
Case 2:
2

题意:

输入n,p,q,然后输入n个数

其中有1个数出现了q次,其他数出现了p次,找出这个出现了q次的数

思路:

把每个数表示成p进制,问题化为每个数都小于p的简单情况

如果每个数都小于p,那么 要找的数*q 和 所有数的和 是关于p同余的

再根据q mod p的逆元即可求出要找的数

代码:
 

#include<iostream>
using namespace std;
 
int main()
{
	int k, n, p, q, x, ni, a, ans, sum[30];
	cin >> k;
	for (int c = 1; c <= k; c++)
	{
		cin >> n >> p >> q;
		for (int i = 0; i < 30; i++)sum[i] = 0;
		for (int i = 1; i <= n; i++)
		{
			cin >> x;
			for (int j = 0; x && j < 30; j++)
			{
				sum[j] += x % p;
				x /= p;
			}
		}
		ni = 1, a = 1, ans = 0;
		while (ni*q%p > 1)ni++;//逆元
		for (int i = 0; i < 30; i++)
		{
			sum[i] %= p;
			ans += sum[i] * ni%p*a;
			a *= p;
		}
		cout << "Case " << c << ":\n" << ans << endl;
	}
	return 0;
}

CSU 1810 Reverse

题目:
Description
Bobo has a n digits decimal number D=d 1 d 2…d n (It may have leading zeros).
Let R(i,j) denotes number D with digits between the i-th position and j-th position reversed. That is, R(i,j)=d 1…d i-1 d j d j-1…d i d j+1 d j+2…d n.
Bobo would like to find

modulo (10 9+7).

Input
The input contains at most 30 sets. For each set:
The first line contains an integer n (1≤n≤10 5).
The second line contains n digits d 1 d 2…d n (0≤d i≤9).
Output
For each set, an integer denotes the result.
Sample Input
2
12
3
012
10
0123456789
Sample Output
45
369
733424314

这个题目给的是5秒,估计θ(n log n)的算法也是可以过的。
不过很多人都是用θ(n)的算法过的,我也是的。
假设某个数字左边有k个数字,右边有s个,那么k+s=n-1
假设mik=10^k,mis=10^s,那么这个数字的贡献是(k(k+1)/2+s(s+1)/2)*mis+(mis*10-1)/9+(mik*10-1)/9
m9是9的逆元,m10是10的逆元,这2个逆元是可以手算出来的,当然,编程算出来也很快。
有了m9就只有乘法没有除法了,有了m10,mik和mis都可以迭代着计算,而不需要快速幂了。
main函数里面唯一一次调用快速幂函数mi是初始化mis = mi(n - 1)
这样,只需要扫描一遍数组就可以得到答案了。
其实字符数组是不需要的,可以一个个字符输入,不存,直接计算。

代码:
 

#include<iostream>
#include<string.h>
using namespace std;

int mod = 1000000007, m9 = 111111112, m10 = 700000005;
long long n, mik, mis;
char c[100001];

long long mi(int k)
{
	if (k == 0)return 1;
	long long s = mi(k / 2);
	s = s*s % mod;
	if (k % 2)s *= 10;
	return s%mod;
}

int main()
{
	while (cin >> n)
	{
		scanf("%s", c);
		mik = 1, mis = mi(n - 1);
		long long s = 0;
		for (long long i = 0; i < n; i++)
		{
		long long s1 = (mis * 10 - 1) *m9 %mod *(mik * 10 % mod - 1) % mod *m9 %mod;
			s1 += (i*(i + 1) / 2 + (n - 1 - i)*(n - i) / 2)*mis % mod;
			s = (s + (c[i] - '0')*s1) % mod;
			mik = mik * 10 % mod;
			mis = mis * m10 % mod;
		}
		cout << s << endl;
	}
	return 0;
}

HDU 5698 瞬间移动

题目:

有一个无限大的矩形,初始时你在左上角(即第一行第一列),每次你都可以选择一个右下方格子,并瞬移过去(如从下图中的红色格子能直接瞬移到蓝色格子),求到第nn行第mm列的格子有几种方案,答案对1000000007取模。 

Input多组测试数据。 

两个整数n,m(2≤n,m≤100000)n,m(2≤n,m≤100000) 

Output

一个整数表示答案

Sample Input

4 5

Sample Output

10

首先,很容易求出答案是(m+n-4)!/(m-2)!/(n-2)!
然后就是想办法打表了。
肯定不是真的把阶乘都存起来,因为10000!的十进制有35660位,100000!的位数就更是多的多。
既然这个题目是模1000000007的,那么阶乘也是可以用模1000000007来存的。
最后只剩下1个问题,在模运算下,如何计算除法,用逆元。

代码:
 

#include<iostream>
using namespace std;
 
int p = 1000000007;
long long fac[200000];
long long anti[100000];
 
long long get_mi(long long n,int k)
{
	if (k == 0)return 1;
	long long r = get_mi(n, k / 2) % p;
	r = (r*r) % p;
	if (k % 2)r = (r*n) % p;
	return r;
}
 
void build()
{
	fac[0] = 1;
	anti[0] = 1;
	for (int i = 1; i < 100000; i++)
	{
		fac[i] = (fac[i - 1] * i) % p;
		anti[i] = get_mi(fac[i], p - 2);	//费马
	}
	for (int i = 100000; i <200000; i++)fac[i] = (fac[i - 1] * i) % p;
}
 
int main()
{
	build();
	int n, m;
	while (cin >> n >> m)
	{
		long long r = fac[m + n - 4];
		r = (r*anti[m - 2]) % p;
		r = (r*anti[n - 2]) % p;
		cout << r << endl;
	}
	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值