2021_GDUT_新生专题训练_数论

知识总结

我的数论知识真的很匮乏,题目做的也不算多。

还是以分析一道题目,来展示思维过程:
传送门

Given a number N, you are asked to count the number of integers between A and B inclusive which are relatively prime to N.
INPUT:
The first line on input contains T (0 < T <= 100) the number of test cases, each of the next T lines contains three integers A, B, N where (1 <= A <= B <= 1e15) and (1 <=N <= 1e9).

这是寒训数论最后一题,巨水 ,应该是容斥板子题

四步走大法:

  1. 看范围。
    1e15,肯定是数论的根号算法之类的。
    如果范围小点还以为是计数dp

  2. 想思路
    找某范围中,【与n互质】的数个数
    一般来说,质数相关的题,给你一个数,基本就是要【分解质因子】。
    分解出来之后,由于是一段区间,按套路,一般考虑【倍数】的筛法,n的质因子的倍数就肯定不是互质了。
    【连环套路】来了
    倍数筛基本就会筛重复,想一下埃筛的两个log
    所以要用【容斥原理】
    【第三重套路】
    一般算[A, B]区间里面,符合某性质的数,都考虑求一个[1, N],然后直接[1, B] - [1, A - 1]就得出答案。

  3. 完善细节
    套【容斥板子】就完了

  4. 解释下【容斥板子】
    代码:

	int res = 0;
	for(int i = 1; i < 1 << m; i ++)
	{
		int t = 1, s = 0; //t total、s count 
		for(int j = 0; j < m; ++j)
		{
			if(i >> j & 1)
			{
				if((LL)t * p[j] > n)
				{
					t = -1;
					break;
				}
				t *= p[j];
				s ++;
			}
		}
		if(t != -1)
		{
			if(s % 2)
				res += n / t;
			else
				res -= n / t;
		}
	}

作用: 在1~n之间,找出能被任一p[i]整除的数的个数。

套板子不欢乐嘛O(∩_∩)O

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long LL;

const int N = 1e3 + 10;

int n, k;
LL p[N];
LL m;
LL MX;

void divide(int n)
{
	m = 0;
	for(int i = 2; i <= n / i; i ++)
	{
		if(n % i == 0)
		{
			p[m ++] = i;
			
			while(n % i == 0)
				n /= i;
		}
	}
	if(n > 1)
		p[m ++] = n; -1;
}

LL cal(LL x)
{
	LL res = 0;
	for(LL i = 1; i < 1 << m; i ++)
	{
		LL t = 1, s = 0; //t total、s count 
		for(int j = 0; j < m; j ++)
		{
			if(i >> j & 1)
			{
				t *= p[j];
				s ++;
			}
		}
		if(s % 2)
			res += x / t;
		else
			res -= x / t;
	}
	return x - res;
}

int main()
{
	int t;
	cin >> t;
	
	for(int i = 1; i <= t; i ++)
	{
		int n;
		LL a, b;
		cin >> a >> b >> n;
		
		divide(n);
		
		printf("Case #%d: %I64d\n", i, cal(b) - cal(a - 1));
	}
	
	return 0;
}

题解

1.A/B

传送门
思路:
水题直接快速幂算

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;

const int N = 1e5+11, mod = 9973;

char str[N];

int qpow(int a, int b, int p)
{
	int res = 1;
	while(b)
	{
		if(b & 1)
			res = (LL)res * a % p;
		a = (LL)a * a %p;
		b >>= 1;
	}
	return res;
}

int mi;
int main()
{
	int t;
	
	scanf("%d", &t);
	
	while(t--)
	{
		int n, b;
		
		scanf("%d%d", &n, &b);
		
		cout<<(n*qpow(b, mod-2, mod)%mod)<<endl;
	}
	
	
	return 0;
}

2.F - 青蛙的约会

思路:
古老的经典题,直接扩欧解决。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long ll;

const int N = 200010;

ll exgcd(ll a, ll b, ll& x, ll& y)
{
	if(!b)
	{
		x = 1;
		y = 0;
		return a;
	}
	ll d = exgcd(b, a%b, y, x);
	y -= a/b*x;
	
	return d;
 } 

int main()
{
	//(m-n)·t-L·b=(y-x)
	ll x, y;
	ll x1, y1, m, n, L;
	scanf("%lld%lld%lld%lld%lld", &x1, &y1, &m, &n, &L);
	ll d = exgcd(m-n, L, x, y);
	
	if((y1-x1)%d)
		printf("Impossible");
	else
	{
	    int k = abs((int)(L/d));
		printf("%lld", (x*(y1-x1)/d%k+k)%k);
	} 
		
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值