知识总结
我的数论知识真的很匮乏,题目做的也不算多。
还是以分析一道题目,来展示思维过程:
传送门
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).
这是寒训数论最后一题,巨水 ,应该是容斥板子题
四步走大法:
-
看范围。
1e15,肯定是数论的根号算法之类的。
如果范围小点还以为是计数dp -
想思路
找某范围中,【与n互质】的数个数
一般来说,质数相关的题,给你一个数,基本就是要【分解质因子】。
分解出来之后,由于是一段区间,按套路,一般考虑【倍数】的筛法,n的质因子的倍数就肯定不是互质了。
【连环套路】来了
倍数筛基本就会筛重复,想一下埃筛的两个log
所以要用【容斥原理】
【第三重套路】
一般算[A, B]区间里面,符合某性质的数,都考虑求一个[1, N],然后直接[1, B] - [1, A - 1]就得出答案。 -
完善细节
套【容斥板子】就完了 -
解释下【容斥板子】
代码:
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;
}