[BZOJ3561]DZY Loves Math VI 题解
题目地址:BZOJ:Problem 3561. — DZY Loves Mat …
May all the beauty be blessed.
题目地址:洛谷:【P2522】[HAOI2011]Problem b – 洛谷、BZOJ:Problem 2301. — [HAOI2011]Problem b
对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。
输入格式:
第一行一个整数n,接下来n行每行五个整数,分别表示a、b、c、d、k
输出格式:
共n行,每行一个整数表示满足要求的数对(x,y)的个数
输入样例#1:
2 2 5 1 5 1 1 5 1 5 2
输出样例#1:
14 3
100%的数据满足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000
这个题[POI2007]ZAP-QUERIES的一个强化版。我们考虑使用容斥原理,答案即ans([1, b], [1, d])-ans([1, a-1], [1, d])-ans([1, b], [1, c-1])+ans([1, a-1], [1, c-1])。
总复杂度O(n \sqrt{n})。
// Code by KSkun, 2018/6
#include <cstdio>
#include <cctype>
#include <algorithm>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
? EOF : *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
register char c = fgc();
while(!isdigit(c)) {
if(c == '-') neg = -1;
c = fgc();
}
while(isdigit(c)) {
res = (res << 1) + (res << 3) + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXN = 50005;
bool notprime[MAXN];
int prime[MAXN], miu[MAXN], ptot;
inline void sieve() {
miu[1] = 1;
for(int i = 2; i < MAXN; i++) {
if(!notprime[i]) {
prime[ptot++] = i;
miu[i] = -1;
}
for(int j = 0; j < ptot && prime[j] * i < MAXN; j++) {
int k = prime[j] * i;
notprime[k] = true;
if(i % prime[j] == 0) {
miu[k] = 0; break;
} else {
miu[k] = -miu[i];
}
}
}
for(int i = 2; i < MAXN; i++) {
miu[i] += miu[i - 1];
}
}
inline LL cal(int b, int d, int k) {
LL all = 0;
if(!k) return 0;
b /= k; d /= k;
if(b > d) std::swap(b, d);
for(int i = 1; i <= b;) {
int j = std::min(b / (b / i), d / (d / i));
all += 1ll * (miu[j] - miu[i - 1]) * (b / i) * (d / i);
i = j + 1;
}
return all;
}
int T, a, b, c, d, k;
int main() {
sieve();
T = readint();
while(T--) {
a = readint(); b = readint(); c = readint(); d = readint(); k = readint();
printf("%lld\n", cal(b, d, k) - cal(a - 1, d, k) - cal(b, c - 1, k)
+ cal(a - 1, c - 1, k));
}
return 0;
}
在数论中,对正整数n,欧拉函数\varphi(n)是小于或等于n的正整数中与n互质的数的数目。
规定:\varphi(1) = 1
若a是一个整数,p是一个质数,则
a^{p-1} \equiv 1 \ (mod \ p)
若n,a为正整数,且gcd(n, a) = 1,则
a^{\varphi(n)} \equiv 1 \ (mod \ n)
欧拉函数的值计算有以下公式
\varphi(x) = x (1 - \frac{1}{p_1}) (1 - \frac{1}{p_2}) \cdots (1 - \frac{1}{p_k})
其中p_i代表x的质因数。
因此我们可以枚举x的质因数,并计算\varphi(x)的值。
这个算法单次计算的复杂度是O(\sqrt{n})。
本方法适用于求1至n的函数值计算。
首先,我们知道欧拉函数是一个积性函数,即\varphi(nm) = \varphi(n) \varphi(m)。
对于素数,它的函数值就是它本身减1。对于非素数,我们想到了线性筛法的避免重复计算条件:prime[j]是i的一个因数。此时,我们已经知道了\varphi(prime[j])和\varphi(i),那么可以算出\varphi(i \cdot prime[j]) = \varphi(i) \varphi(prime[j])。
这个算法的复杂度是O(n)。
代码实现如下:
inline void phi_solve(int n) {
phi[1] = 1;
for(int i = 2; i <= n; i++) {
if(!vis[i]) {
prime[tot++] = i;
phi[i] = i - 1;
}
for(int j = 0; j < tot; j++) {
vis[i * prime[j]] = true;
if(i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];
break;
} else {
phi[i * prime[j]] = phi[i] * phi[prime[j]];
}
}
}
}
若a,p为正整数,且gcd(a, p) = 1,称满足a^r \equiv 1 \ (mod \ p)的最小正整数r为a模p的阶,记作Ord_p(a)。
没什么好办法,从小到大枚举r并判断是否是阶。
若a,p为正整数,且gcd(a, p) = 1,称满足Ord_p(a) = \varphi(p)的a叫做模p的一个原根。
注意:不是任何p都存在原根。每个素数都存在原根。
我们知道了上述阶的性质2,要判断某数是不是模m的原根,可以验证是否存在m-1的一个约数a满足g^a \equiv 1 \ (mod \ m),如果有的话那么g的阶就不是m-1,则g不是模m的原根。
一般来说原根不会很大,从2开始枚举并按照上述方法判断就可以得到。
代码如下
inline int solve_g(int m) {
// 预处理出因数
int fact[105], tot = 0, x = m - 1;
for(int i = 2; i * i <= x; i++) {
if(x % i == 0) {
fact[tot++] = (m - 1) / i;
while(x % i == 0) x /= i;
}
}
if(x > 1) fact[tot++] = (m - 1) / x;
// 枚举原根
bool success;
for(int i = 2;; i++) {
success = true;
for(int j = 0; j < tot; j++) {
if(fpow(i, fact[j], m) == 1) {
success = false;
break;
}
}
if(success) return i;
}
}
注:代码中fpow()
函数是快速幂取模。
我们知道,任何一个小于正整数m的正整数都可以表示为原根的某次幂,那么求一个由小于m正整数组成的数列的累乘值可以转化为原根的指数求和,这样可以不用计算高精度,且由周期性还可以缩小指数的范围。
小C有一个集合S,里面的元素都是小于M的非负整数。他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S。小C用这个生成器生成了许多这样的数列。但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个。小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi。另外,小C认为这个问题的答案可能很大,因此他只需要你帮助他求出答案mod 1004535809的值就可以了。
输入格式:
一行,四个整数,N、M、x、|S|,其中|S|为集合S中元素个数。第二行,|S|个整数,表示集合S中的所有元素。
输出格式:
一行,一个整数,表示你求出的种类数mod 1004535809的值。
输入样例#1:
4 3 1 2 1 2
输出样例#1:
8
【样例说明】
可以生成的满足要求的不同的数列有(1,1,1,1)、(1,1,2,2)、(1,2,1,2)、(1,2,2,1)、(2,1,1,2)、(2,1,2,1)、(2,2,1,1)、(2,2,2,2)。
【数据规模和约定】
对于10%的数据,1<=N<=1000;
对于30%的数据,3<=M<=100;
对于60%的数据,3<=M<=800;
对于全部的数据,1<=N<=109,3<=M<=8000,M为质数,1<=x<=M-1,输入数据保证集合S中元素不重复
对于本题我们需要求出数列中所有元素的乘积,我们可以应用乘法转加法得到数列乘积的原根指数,再将x处理成原根的某次幂,根据阶的性质3即可判定是否同余。
本题的完整题解参见[SDOI2015]序列统计 题解 | KSkun’s Blog。
当且仅当p为素数时,有 (p-1)! \equiv -1 \pmod{p} 。
积性函数是指一个定义域为正整数n的数论函数f(n),有如下性质:
\begin{aligned} & f(1) = 1 \\ & f(ab) = f(a)f(b) & (a, b) = 1 \end{aligned}
如果上述性质中,不要求a、b互质,即不只a、b互质的情况下都有f(ab) = f(a)f(b),则f(n)为完全积性函数。
更多内容可以参见积性函数 – 维基百科,自由的百科全书