O ( n l o g l o g n ) O(nloglogn) O(nloglogn)写法:用每个数筛去他的倍数
O ( n ) O(n) O(n)写法:利用每个合数都能被表示成一系列素数积
int prime[maxn], cnt;
int vis[maxn];
void solve(int n) {
vis[0] = vis[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!vis[i])
prime[cnt++] = i;
for (int j = 0; j < cnt && i * prime[j] <= n; ++j) {
vis[i * prime[j]] = 1;
if (i % prime[j] == 0)
break;
}
}
}
参考链接
若正整数a和n互质,则 a φ ( n ) ≡ 1 ( m o d n ) a^{φ(n)}≡1(modn) aφ(n)≡1(modn)其中φ(n)为1~n中与n互质的数的个数
若正整数a和n互质,对于任意正整数b,满足 a b ≡ a b m o d φ ( n ) ( m o d n ) a^b≡a^{bmodφ(n)}(modn) ab≡abmodφ(n)(modn);可以用来对于求幂运算时缩小数据范围和计算次数;特别的若a和n不互质,则 a b ≡ a b m o d φ ( n ) + φ ( n ) ( m o d n ) a^b≡a^{bmodφ(n)+φ(n)}(modn) ab≡abmodφ(n)+φ(n)(modn)
对于任意互质的正整数a和n,满足 a x ≡ 1 ( m o d n ) a^x≡1(modn) ax≡1(modn)的最小整数 x x x是 φ ( n ) φ(n) φ(n)的约数
根据欧拉函数定义 ϕ ( n ) = n ∏ i = 0 s ( 1 − 1 p i ) \phi(n)=n\prod\limits_{i=0}^s(1-\frac{1}{p_i}) ϕ(n)=ni=0∏s(1−pi1),其中 p i p_i pi是 n n n的质因子
int euler_phi(int n) {
int m = int(sqrt(n + 0.5));
int ans = n;
for (int i = 2; i <= m; i++)
if (n % i == 0) {
ans = ans / i * (i - 1);
while (n % i == 0) n /= i;
}
if (n > 1) ans = ans / n * (n - 1);
return ans;
}
对于质数p,任意整数a,均满足 a p ≡ a ( m o d p ) a^p≡a(modp) ap≡a(modp) ; 属于欧拉定理的特例
typedef long long ll;
ll mod_mul(ll a, ll b, ll c) {
ll rhs = 0;
while (b) {
if (b & 1)
rhs = (rhs + a) % c;
b >>= 1;
a = (a + a) % c;
}
return rhs;
}
ll mod_exp(ll a, ll b, ll c) {
ll rhs = 1;
while (b) {
if (b & 1)
rhs = rhs * a % c;
b >>= 1;
a = a * a % c;
}
return rhs;
}
bool Miller_Rabin(ll n, int respat) {
if (n == 2ll || n == 3ll || n == 5ll || n == 7ll || n == 11ll)
return true;
if (n == 1 || !(n % 2) || !(n % 3) || !(n % 5) || !(n % 7) || !(n % 11))
return false;
int k = 0;
ll d = n - 1;
while (!(d & 1ll)) {
k++; d >>= 1ll;
}
srand((ll)time(0));
for (int i = 0; i < respat; ++i) {
ll a = rand() % (n - 2) + 2;
ll x = mod_exp(a, d, n);
ll y = 0ll;
//二次探测,利用x^2≡1(modn)时只有x=1或x=n-1两个解
for (int j = 0; j < k; ++j) {
y = mod_mul(x, x, n);
if (1ll == y && 1ll != x && n - 1ll != x)
return false;
x = y;
}
if (1ll != y)
return false;
}
return true;
}
int main() {
ll x; scanf("%lld", &x);
if (Miller_Rabin(x, 6))
printf("Yes\n");
else
printf("No\n");
}
a x ≡ b ( m o d m ) ax≡b(mod m) ax≡b(modm),转换为 a x + m y = b ax+my=b ax+my=b,使用 e x g c d exgcd exgcd求解,注意有多解
typedef long long ll;
ll exgcd(ll a, ll b, ll &x, ll &y) {
if(b == 0) {
x = 1;
y = 0;
return a;
}
ll r = exgcd(b, a % b, x, y);
ll tmp = x;
x = y;
y = tmp - a / b * y;
return r;
}
//ax = b(mod n) -> ax - ny = b
vector line_mod_quation(ll a, ll b, ll n) {
ll x, y;
ll d = exgcd(a, n, x, y);
vector ans;
ans.clear();
if(b % d == 0) {
x %= n; x += n; x %= n;
ans.push_back(x * (b / d) % (n / d));
for(int i = 1; i <= d; i++) ans.push_back((ans[0] + i * n / d) % n);
}
return ans;
}
解决一元线性同余方程 x ≡ a i ( m o d m i ) x≡a_i(mod m_i) x≡ai(modmi),其中 m i m_i mi是两两互质的数
1. 令$M=\prod_{i=1}^n{m_i}$,$Mi={\frac{M}{m_i}}$,$t_i$是$M_i*t_i≡1(modm_i)$的一个解
/*
x = a1(mod m1)
x = a2(mod m2)
.
x = an(mod mn)
其中m1到mn两两互质的整数
*/
ll exgcd(ll a, ll b, ll &x, ll &y) {
if(b == 0) {
x = 1;
y = 0;
return a;
}
ll r = exgcd(b, a % b, x, y);
ll tmp = x;
x = y;
y = tmp - a / b * y;
return r;
}
ll CRT(int n, int a[], int m[]) {
ll M = 1, x = 0, xx, yy;
for(int i = 0; i < n; i++) M *= m[i];
for(int i = 0; i < n; i++) {
ll w = M / m[i];
exgcd(m[i], w, xx, yy);
x = (x + yy * w * a[i]) % M;
}
return (x + M) % M;
}
使用与 m i m_i mi不为互质的一元线性同余方程组
ll ex_gcd(ll a, ll b, ll& x, ll& y) {
if (a == 0 && b == 0) return -1; // 无最大公因数
ll d = a;
if (b != 0)
d = ex_gcd(b, a % b, y, x), y -= x * (a / b);
else
x = 1, y = 0;
return d;
}
// mod不满足两两互质
// 通解为 re + k*M
// 返回最小非负整数解
bool excrt(ll r[], ll m[], int n, ll& re, ll& M) {
ll x, y;
M = m[0], re = r[0];
for (int i = 1; i < n; i++) {
ll d = ex_gcd(M, m[i], x, y);
if ((r[i] - re) % d != 0) return 0;
x = (r[i] - re) / d * x % (m[i] / d);
re += x * M;
M = M / d * m[i];
re = re % M;
}
re = (re + M) % M;
return 1;
}
定义式子 x 2 ≡ n ( m o d p ) x^2≡n(modp) x2≡n(modp),给出n和p,是否存在一个式子满足该式子,即模p意义下的开根 n \sqrt n n参考链接
#define random(a,b) (rand()%(b-a+1)+a)
ll quickmod(ll a, ll b, ll c) {
ll ans = 1;
while (b) {
if (b & 1)
ans = ans * a % c;
b >>= 1;
a = a * a % c;
}
return ans;
}
ll p, w;
struct QuadraticField
{
ll x, y;
QuadraticField operator *(QuadraticField T) {
QuadraticField rhs;
rhs.x = (this->x * T.x % p + this->y * T.y % p * w % p) % p;
rhs.x = (rhs.x + p) % p;
rhs.y = (this->x * T.y % p + this->y * T.x % p) % p;
rhs.y = (rhs.y + p) % p;
return rhs;
}
QuadraticField operator ^(ll b) {
QuadraticField rhs;
QuadraticField a = *this;
rhs.x = 1; rhs.y = 0;
while (b) {
if (b & 1)
rhs = rhs * a;
b >>= 1;
a = a * a;
}
return rhs;
}
};
ll Legender(ll a) {
ll rhs = quickmod(a, (p - 1) / 2, p);
if (rhs + 1 == p)
return -1;
else
return rhs;
}
ll get_w(ll n, ll a) {
return ((a * a % p - n) % p + p) % p;
}
ll solve(ll n) {
ll a;
if (p == 2)
return n;
if (Legender(n) == -1)
return -1;
srand((unsigned)time(NULL));
while (true) {
a = random(0, p - 1);
w = get_w(n, a);
if (Legender(w) == -1)
break;
}
QuadraticField ans, rhs;
rhs.x = a; rhs.y = 1;
ans = rhs ^ ((p + 1) / 2);
return ans.x;
}
int main()
{
int t; scanf("%d", &t);
while (t--) {
ll n; scanf("%lld%lld", &n, &p);
n %= p;
if (n == 0)
printf("0\n");
else {
ll ans1 = solve(n), ans2;
if (ans1 == -1)
printf("Hola!\n");
else {
ans2 = p - ans1;
if (ans1 == ans2)
printf("%lld\n", ans1);
else
printf("%lld %lld\n", min(ans1, ans2), max(ans1, ans2));
}
}
}
}
( a , p ) = 1 (a, p)=1 (a,p)=1时,满足 a g ≡ 1 ( m o d p ) a^g≡1(mod p) ag≡1(modp)的最小的 g g g正好等于 φ ( p ) φ(p) φ(p)
int powmod(int a, int b, int p) {
int res = 1;
while (b) {
if (b & 1)
res = res * a % p;
b >>= 1;
a = a * a % p;
}
return res;
}
//找原根
int generator(int p) {
vector fact;
int phi = p - 1, n = phi;
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) {
fact.push_back(i);
while (n % i == 0)
n /= i;
}
}
if (n > 1)
fact.push_back(n);
for (int res = 2; res <= p; ++res) {
bool ok = true;
for (int factor : fact) {
if (powmod(res, phi / factor, p) == 1) {
ok = false;
break;
}
}
if (ok)
return res;
}
return -1;
}
int sq = (int)sqrt(p + .0) + 1;
vector> dec(sq);
for (int i = 1; i <= sq; ++i)
dec[i - 1] = { powmod(a, i * sq * k % (p - 1), p), i };
sort(dec.begin(), dec.end());
int any_ans = -1;
for (int i = 0; i < sq; ++i) {
int my = powmod(a, i * k % (p - 1), n) * b % n;
auto it = lower_bound(dec.begin(), dec.end(), make_pair(my, 0));
if (it != dec.end() && it->first == my) {
any_ans = it->second * sq - i;
break;
}
}
int gcd(int a, int b) { return a ? gcd(b % a, a) : b; }
int powmod(int a, int b, int p) {
int res = 1;
while (b) {
if (b & 1)
res = res * a % p;
b >>= 1;
a = a * a % p;
}
return res;
}
//找原根
int generator(int p) {
vector fact;
int phi = p - 1, n = phi;
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) {
fact.push_back(i);
while (n % i == 0)
n /= i;
}
}
if (n > 1)
fact.push_back(n);
for (int res = 2; res <= p; ++res) {
bool ok = true;
for (int factor : fact) {
if (powmod(res, phi / factor, p) == 1) {
ok = false;
break;
}
}
if (ok)
return res;
}
return -1;
}
//求x^k=a(modn)的所有解
int main()
{
int n, k, a; scanf("%d%d%d", &n, &k, &a);
if (a == 0)
return puts("1\n0"), 0;
int g = generator(n);
int sq = (int)sqrt(n + .0) + 1;
vector> dec(sq);
for (int i = 1; i <= sq; ++i)
dec[i - 1] = { powmod(g, i * sq * k % (n - 1), n), i };
sort(dec.begin(), dec.end());
int any_ans = -1;
for (int i = 0; i < sq; ++i) {
int my = powmod(g, i * k % (n - 1), n) * a % n;
auto it = lower_bound(dec.begin(), dec.end(), make_pair(my, 0));
if (it != dec.end() && it->first == my) {
any_ans = it->second * sq - i;
break;
}
}
if (any_ans == -1)
return puts("0"), 0;
int delta = (n - 1) / gcd(k, n - 1);
vector ans;
for (int cur = any_ans % delta; cur < n - 1; cur += delta)
ans.push_back(powmod(g, cur, n));
sort(ans.begin(), ans.end());
printf("%d\n", ans.size());
for (int answer : ans)
printf("%d ", answer);
}
在人数为6的人群中,一定有三个人彼此相识,或者彼此不认识 => 对6个顶点的完全图的边用红,蓝两色着色,结果至少有两个同色的三角形
设a,b为正整数,令 N ( a , b ) N(a, b) N(a,b)使保证有a个人彼此相识或者有b个人彼此不相识所需要的最小人数,则称 N ( a , b ) N(a, b) N(a,b)为Ramsey数,满足 { N ( a , b ) = N ( b , a ) N ( a , 2 ) = a N ( a , b ) ≤ N ( a − 1 , b ) + N ( a , b − 1 ) 特 别 的 当 N ( a − 1 , b ) 和 N ( a , b − 1 ) 都 为 偶 数 时 , N ( a , b ) ≤ N ( a − 1 , b ) + N ( a , b − 1 ) − 1 \begin{cases}N(a, b)=N(b,a)\\N(a,2)=a\\N(a,b)\leq N(a-1,b)+N(a,b-1)\\特别的当N(a-1,b)和N(a,b-1)都为偶数时,\\N(a,b)\leq N(a-1,b)+N(a,b-1)-1\end{cases} ⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧N(a,b)=N(b,a)N(a,2)=aN(a,b)≤N(a−1,b)+N(a,b−1)特别的当N(a−1,b)和N(a,b−1)都为偶数时,N(a,b)≤N(a−1,b)+N(a,b−1)−1
由容斥系数所构成的函数,莫比乌斯函数 μ ( d ) = { 1 , d = 1 ( − 1 ) k , d = p 1 p 2 . . . p k 0 , 其 他 \mu (d)=\begin{cases} 1,d=1\\(-1)^k,d=p_1p_2...p_k\\ 0,其他 \end{cases} μ(d)=⎩⎪⎨⎪⎧1,d=1(−1)k,d=p1p2...pk0,其他
满足 ( f ∗ g ) ( n ) = ∑ d ∣ n f ( d ) ∗ g ( n d ) (f*g)(n)=\sum_{d|n}f(d)*g({\frac{n}{d}}) (f∗g)(n)=∑d∣nf(d)∗g(dn);其中满足互质的整数 a , b , f ( a b ) = f ( a ) f ( b ) a,b,f(ab)=f(a)f(b) a,b,f(ab)=f(a)f(b)的称为积性函数;满足任意正整数 a , b , f ( a b ) = f ( a ) f ( b ) a,b,f(ab)=f(a)f(b) a,b,f(ab)=f(a)f(b)成为完全积性函数
{ μ ( n ) , 莫 比 乌 斯 函 数 ϕ ( n ) , 欧 拉 函 数 d ( n ) , 约 数 个 数 d ( n ) = ∑ d ∣ n 1 σ ( n ) , 约 数 和 σ ( n ) = ∑ d ∣ n d \begin{cases} \mu(n),莫比乌斯函数\\ \phi(n),欧拉函数\\ d(n),约数个数d(n)=\sum_{d|n}1\\ \sigma(n),约数和\sigma(n)=\sum_{d|n}d \end{cases} ⎩⎪⎪⎪⎨⎪⎪⎪⎧μ(n),莫比乌斯函数ϕ(n),欧拉函数d(n),约数个数d(n)=∑d∣n1σ(n),约数和σ(n)=∑d∣nd
{ ϵ ( n ) = [ n = = 1 ] , 元 函 数 I ( n ) = 1 , 恒 等 函 数 i d ( n ) = n , 单 元 函 数 \begin{cases} \epsilon(n)=[n==1],元函数\\ I(n)=1,恒等函数\\ id(n)=n,单元函数 \end{cases} ⎩⎪⎨⎪⎧ϵ(n)=[n==1],元函数I(n)=1,恒等函数id(n)=n,单元函数
求所有由a~z组成的长度为n的字符串中,没有周期性的字符串数量。重复性是指一个长度为n的字符串可以由一个长度为m
设 f ( n ) f(n) f(n)为周期为n的因子的字符串的数量,每个位置有26个字母的选择,所以数量为 2 6 n 26^n 26n;设 g ( n ) g(n) g(n)为周期为n的字符串的数量;则 f ( n ) = ∑ m ∣ n g ( m ) f(n)=\sum_{m|n}g(m) f(n)=∑m∣ng(m)
根据莫比乌斯反演,得到 g ( n ) = ∑ d ∣ n f ( n d ) μ ( d ) = ∑ d ∣ n 2 6 n d μ ( d ) g(n)=\sum_{d|n}f({\frac{n}{d}})\mu(d)=\sum_{d|n}26^{\frac{n}{d}}\mu(d) g(n)=∑d∣nf(dn)μ(d)=∑d∣n26dnμ(d)
因为n很大,无法打出全部的 μ \mu μ值;根据 μ \mu μ的定义,我们质因数分解 n n n,枚举质因子处理出所有的因子的 μ \mu μ值
注意 n < = 1 0 9 n<=10^9 n<=109的约数个数最大值也只有800左右
const int mod = 10009;
int mod_pow(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = res * a % mod;
b >>= 1;
a = a * a % mod;
}
return res;
}
map moebius(int n) {
map res;
vector primes;
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) {
primes.push_back(i);
while (n % i == 0) n /= i;
}
}
if (n != 1) primes.push_back(n);
int m = primes.size();
for (int i = 0; i < (1 << m); ++i) {
int mu = 1, d = 1;
for (int j = 0; j < m; ++j) {
if (i >> j & 1) {
mu *= -1;
d *= primes[j];
}
}
res[d] = mu;
}
return res;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0);
int n; cin >> n;
int res = 0;
map mu = moebius(n);
for (auto it = mu.begin(); it != mu.end(); ++it) {
res += it->second * mod_pow(26, n / it->first);
res = (res + mod) % mod;
}
cout << res << '\n';
}
处理前缀和问题,只适用于积性函数,其中两个积性函数的积也为积性函数
计算 ∑ i = 1 n f ( i ) \sum_{i=1}^nf(i) ∑i=1nf(i)
若 h ( i ) h(i) h(i)很容易求出,那么只需要对后面 S ( n d ) S({\frac{n}{d}}) S(dn)除法分块,复杂度为 O ( n 2 3 ) O(n^{2\over 3}) O(n32)
S ( n ) = ∑ i = 1 n μ ( i ) S(n)=\sum_{i=1}^n\mu(i) S(n)=∑i=1nμ(i)
根据 μ ∗ I = ϵ \mu*I=\epsilon μ∗I=ϵ,则 h = ϵ , f = μ , g = I h=\epsilon,f=\mu,g=I h=ϵ,f=μ,g=I
则 S ( n ) = 1 − ∑ i = 1 n S ( n d ) S(n)=1-\sum_{i=1}^nS({\frac{n}{d}}) S(n)=1−∑i=1nS(dn)
S ( n ) = ∑ i = 1 n ϕ ( i ) S(n)=\sum_{i=1}^n\phi(i) S(n)=∑i=1nϕ(i)
根据 ϕ ∗ I = i d \phi*I=id ϕ∗I=id
S ( n ) = ∑ i = 1 n i − ∑ d = 2 n S ( n d ) S(n)=\sum_{i=1}^ni-\sum_{d=2}^nS(\frac{n}{d}) S(n)=∑i=1ni−∑d=2nS(dn)
const int maxn = 1e6 + 5;
int phi[maxn], mu[maxn], vis[maxn];
int sum_mu[maxn], sum_phi[maxn];
vector prime;
unordered_map phi_mp;
unordered_map mu_mp;
void get(int n) {
phi[1] = mu[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!vis[i]) {
prime.push_back(i);
phi[i] = i - 1; mu[i] = -1;
}
for (int j = 0; j < prime.size() && prime[j] * i <= n; ++j) {
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];
mu[i * prime[j]] = 0;
break;
}
else {
mu[i * prime[j]] = -mu[i];
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
}
for (int i = 1; i <= n; ++i) {
sum_mu[i] = sum_mu[i - 1] + mu[i];
sum_phi[i] = sum_phi[i - 1] + phi[i];
}
}
int djs_mu(int x) {
if (x <= 1e6)
return sum_mu[x];
if (mu_mp.count(x))
return mu_mp[x];
int ans = 1;
for (int l = 2, r; l >= 0 && l <= x; l = r + 1) {
r = x / (x / l);
ans -= (r - l + 1) * djs_mu(x / l);
}
return mu_mp[x] = ans;
}
long long djs_phi(long long x) {
if (x <= 1e6)
return sum_phi[x];
if (phi_mp.count(x))
return phi_mp[x];
long long ans = x * (x + 1) / 2;
for (long long l = 2, r; l >= 0 && l <= x; l = r + 1) {
r = x / (x / l);
ans -= (r - l + 1) * djs_phi(x / l);
}
return phi_mp[x] = ans;
}
S ( n ) = ∑ i = 1 n i ∗ ϕ ( i ) S(n)=\sum_{i=1}^n{i*\phi(i)} S(n)=∑i=1ni∗ϕ(i)
此时 f = i ∗ ϕ ( i ) f=i*\phi(i) f=i∗ϕ(i),使用迪利克雷卷积打开 f ∗ g = ∑ d ∣ n d ∗ ϕ ( d ) ∗ g ( n d ) f*g=\sum_{d|n}d*\phi(d)*g({\frac{n}{d}}) f∗g=∑d∣nd∗ϕ(d)∗g(dn)
令 g = i d g=id g=id,则 ∑ d ∣ n d ∗ ϕ ( d ) ∗ n d = ∑ d ∣ n ϕ ( d ) ∗ n = n 2 \sum_{d|n}d*\phi(d)*{\frac{n}{d}}=\sum_{d|n}\phi(d)*n=n^2 ∑d∣nd∗ϕ(d)∗dn=∑d∣nϕ(d)∗n=n2
S ( n ) = ∑ i = 1 n i 2 − ∑ d = 2 n d ∗ S ( n d ) S(n)=\sum_{i=1}^ni^2-\sum_{d=2}^nd*S({\frac{n}{d}}) S(n)=∑i=1ni2−∑d=2nd∗S(dn)
总结:首先筛出数据范围根号内的积性函数的前缀后,再递归实现杜教筛,使用unodered_map减少复杂度
设 t = P i , k = P % I t=\frac{P}{i},k=P\%I t=iP,k=P%I
对于 t ∗ i + k ≡ 0 m o d P t*i+k≡0\bmod P t∗i+k≡0modP,等式同除 i ∗ k i*k i∗k
得到 t k + 1 i ≡ 0 m o d P {t\over k}+{1\over i}≡0\bmod P kt+i1≡0modP,即 t ∗ i n v [ k ] + i n v [ i ] ≡ 0 m o d P t*inv[k]+inv[i]≡0\bmod P t∗inv[k]+inv[i]≡0modP
代入 t , k t,k t,k,则 i n v [ i ] = ( P − P i ) ∗ i n v [ P % i ] % P inv[i]=(P-\frac{P}{i})*inv[P\%i]\%P inv[i]=(P−iP)∗inv[P%i]%P
int inv[maxn];
inv[1] = inv[2] = 1;
inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
任何大于4的偶数都能表示为两个质数的和
奇数 n n n只能表示为 2 2 2和 n − 2 n-2 n−2两个质数;否则可以表示为偶数 ( n − 3 ) (n-3) (n−3)和质数 3 3 3,就是 3 3 3个质数的和
解决等价类计数问题,等价关系即满足等价关系的两个元素,还满足自反性、对称性以及传递性。统计等价类的个数:使用置换集合F来描述,如果一个置换将一个方案映射到了另一个方案,说明两者是等价的。
Burnside定理:如果一个方案s经过一个置换f后不变,则称s为不动点,将f的所有不动点数目记为c(f),那么等价类数目等于所有置换的c(f)的平均值。
典型的等价类置换:背景都是由n个珠子构成的环,并且颜色总数为t
n个石头排成一圈,要用m种颜色去染,求有多少种不同的染色方案。旋转之后相同的方案视为一种。输出方案数模 1000000007 1000000007 1000000007, n , m < = 1 0 9 n,m<=10^9 n,m<=109
考虑每种旋转,从旋转0位置、1位置到n-1位置的n种转法。
对计算旋转k位置后与原来相同的方案数,可知 i i i位置的石头颜色要与 ( i + k t ) m o d n (i+kt)modn (i+kt)modn的颜色相同,最小的 t = n g c d ( k , n ) t={\frac{n}{gcd(k,n)}} t=gcd(k,n)n满足条件
与第i个石头相同的石头的集合称为i的轨迹。显然,i的轨迹和(i+k)的轨迹是完全相同的。我们将n个石头划分为若干个互不相干的轨迹。而旋转之后和原来颜色相同的染色方案就是每一条轨迹都染同一个颜色。因此算出轨迹数目就可以知道有多少方案数旋转之后和原来相同了。
每一个轨迹石头的个数是 t = n g c d ( k , n ) t={\frac{n}{gcd(k,n)}} t=gcd(k,n)n,由于不同轨迹中石头数相同,则轨迹数为 n t = g c d ( k , n ) \frac{n}{t}=gcd(k,n) tn=gcd(k,n)。因此旋转k个位置后与原来相同的方案数就是 m g c d ( k , n ) m^{gcd(k,n)} mgcd(k,n)
计算答案时不断枚举k的值,并把 m g c d ( k , n ) m^{gcd(k,n)} mgcd(k,n)的值加起来
优化:由于 g c d ( k , n ) gcd(k,n) gcd(k,n)只有有限个不同的值,所以相同的值放在一起合并统计。设d为n的约数,即要统计满足 g c d ( k , n ) = d gcd(k,n)=d gcd(k,n)=d的k的个数,而 k = d ∗ t k=d*t k=d∗t。则 d = g c d ( k , n ) = g c d ( d ∗ t , n ) = d ∗ g c d ( t , n d ) d=gcd(k,n)=gcd(d*t,n)=d*gcd(t,\frac{n}{d}) d=gcd(k,n)=gcd(d∗t,n)=d∗gcd(t,dn),因此 g c d ( t , n d ) = 1 gcd(t,\frac{n}{d})=1 gcd(t,dn)=1,满足条件的t的个数就是 ϕ ( n d ) \phi(\frac{n}{d}) ϕ(dn)
则答案为 ∑ d ∣ n m d ϕ ( n d ) n \frac{\sum_{d|n}{m^d}{\phi({\frac{n}{d}})}}{n} n∑d∣nmdϕ(dn)
const int mod = 1e9 + 7;
int n, m;
void solve() {
//质因子分解
map primes = primes_factor(n);
//约数
vector divs = divsor(n);
ll res = 0;
for (int i = 0; i < divs.size(); ++i) {
ll euler = divs[i];
for (auto it = primes.begin(); it != primes.end(); ++it) {
int p = it->first;
if (divs[i] % p == 0) euler = euler / p * (p - 1);
}
res += euler * mod_pow(m, n / divs[i]) % mod;
res %= mod;
}
printf("%lld\n", res * mod_pow(n, mod - 2) % mod);
}
求出 n ! n! n!中质因子 x x x的个数,例如求出15当中5的个数?
15 ! = 1 × 2 × ⋯ × 15 15!=1\times2\times\dots\times15 15!=1×2×⋯×15,其中5这个质因子只有在5的倍数中出现,即5、10、15当中 15 5 \frac{15}{5} 515个,除以5又得1、2、3,由于不考虑非5的倍数,继而考虑它的子问题 3 ! 3! 3!即可。
int get_prime_factor(int n, int x) {
if (!n) return 0;
return n / x + get_prime_factor(n / x, x);
}
比如我们需要求出 10 ! 10! 10!最后的非0位,由于质因数2和5组合会在结尾产生0.那么我们将 10 ! 10! 10!中2、5质因数全部去掉(注意考虑多余的2对末位的影响)。如 1 × 2 × 3 × 4 × 5 × 6 × 7 × 8 × 9 × 10 1\times2\times3\times4\times5\times6\times7\times8\times9\times10 1×2×3×4×5×6×7×8×9×10去掉因子2和5后就是 1 × 1 × 3 × 1 × 1 × 3 × 7 × 1 × 9 × 1 1\times1\times3\times1\times1\times3\times7\times1\times9\times1 1×1×3×1×1×3×7×1×9×1,剩下的数字末尾一定是3、7、9、1之一,再考虑多余的2的贡献即可
对于如何求出剩下的一串数字相乘后末位的数字的求法,转化为求出这些数末位为3、7、9出现的次数(由于这些数的次方以4为周期,例如 3 0 = 1 , 3 1 = 3 , 3 2 = 9 , 3 3 = 7 , 3 4 = 1 3^0=1,3^1=3,3^2=9,3^3=7,3^4=1 30=1,31=3,32=9,33=7,34=1),因此只需要求出这串数字末位3、7、9各自出现的次数。
一个数列实际可以分为奇数列和偶数列,以 10 ! = 1 × 2 × ⋯ × 10 10!=1\times2\times\dots\times10 10!=1×2×⋯×10为例,分为 1 3 5 7 9 1\,3\,5\,7\,9 13579和 2 4 6 8 10 2\,4\,6\,8\,10 246810,可以发现实际上 2 4 6 8 10 2\,4\,6\,8\,10 246810中的个数就是 1 2 3 4 5 1\,2\,3\,4\,5 12345中的个数,也就是我们需要解决它的子问题: f ( n ) = f ( n / 2 ) + g ( n ) f(n)=f(n/2)+g(n) f(n)=f(n/2)+g(n), g ( n ) g(n) g(n)表示奇数列中的数目
观察 g ( n ) g(n) g(n): 1 3 5 7 9 11 13 … 1\,3\,5\,7\,9\,11\,13\dots 135791113…实际上也分为 1 3 7 9 11 13 … 1\,3\,7\,9\,11\,13\dots 13791113…以及5的奇数倍 5 15 25 … 5\,15\,25\dots 51525…部分,除以5又得到 1 3 5 7 … 1\,3\,5\,7\dots 1357…子问题;因此统计末位为 x ∈ { 1 , 3 , 7 , 9 } x\in\{1,3,7,9\} x∈{1,3,7,9}的个数即为 g ( n , x ) = n / 10 + ( n % 10 ≥ x ) + g ( n / 5 , x ) g(n,x)=n/10+(n\%10\ge x)+g(n/5,x) g(n,x)=n/10+(n%10≥x)+g(n/5,x)
通过两个递归方程可以 l g ( n ) lg(n) lg(n)的复杂度解出末位为1、3、7、9的个数。通过循环节的性质可以快速求解这串数字 m o d 10 \bmod 10 mod10的结果。
int g(int n, int x) {
if (!n) return 0;
return n / 10 + (n % 10 >= x) + g(n / 5, x);
}
int f(int n, int x) {
if (!n) return 0;
return f(n / 2, x) + g(n, x);
}
//2,3,7,9的循环节,其中注意若2的个数为0的话循环节第一位应该为1
int cyclic_section[][4] = {{6, 2, 4, 8}, {1, 3, 9, 7}, {1, 7, 9, 3}, {1, 9, 1, 9}};
int main() {
int n, m;
while (~scanf("%d%d", &n, &m)) {
int cnt2 = get_prime_factor(n, 2) - get_prime_factor(n - m, 2);
int cnt5 = get_prime_factor(n, 5) - get_prime_factor(n - m, 5);
int cnt3 = f(n, 3) - f(n - m, 3);
int cnt7 = f(n, 7) - f(n - m, 7);
int cnt9 = f(n, 9) - f(n - m, 9);
int ans = 1;
if (cnt5 > cnt2) {
printf("5\n"); continue;
}
if (cnt2 != cnt5) {
ans *= cyclic_section[0][(cnt2 - cnt5) % 4];
ans %= 10;
}
ans *= cyclic_section[1][cnt3 % 4]; ans %= 10;
ans *= cyclic_section[2][cnt7 % 4]; ans %= 10;
ans *= cyclic_section[3][cnt9 % 4]; ans %= 10;
printf("%d\n", ans);
}
}
伯努利数 B n B_n Bn是一个与数论有密切关联的有理数序列。前几项被发现的伯努利数分别是 B 0 = 1 , B 1 = 1 / 2 , B 2 = 1 / 6 , B 3 = 0 , B 4 = − 1 / 30 , B 5 = 0 , B 6 = 1 / 42 , B 7 = 0 , B 8 = − 1 / 30 B_0=1,B_1=1/2,B_2=1/6,B_3=0,B_4=-1/30,B_5=0,B_6=1/42,B_7=0,B_8=-1/30 B0=1,B1=1/2,B2=1/6,B3=0,B4=−1/30,B5=0,B6=1/42,B7=0,B8=−1/30
S m ( n ) = ∑ k = 1 n k m = 1 m + 2 m + ⋯ + n m S_m(n)=\sum_{k=1}^nk^m=1^m+2^m+\dots+n^m Sm(n)=∑k=1nkm=1m+2m+⋯+nm
这个数列和必定为变量为 n n n,次数为 m + 1 m+1 m+1的多项式,称为伯努利多项式。伯努利多项式的系数与伯努利数有着密切关系,如 S m ( n ) = 1 m + 1 ∑ k = 0 m ( m + 1 k ) B k n m + 1 − k S_m(n)=\frac{1}{m+1}\sum_{k=0}^m{m+1\choose k}B_kn^{m+1-k} Sm(n)=m+11∑k=0m(km+1)Bknm+1−k
∑ j = 0 m ( m + 1 j ) B j = 0 \sum_{j=0}^m{m+1\choose j}B_j=0 ∑j=0m(jm+1)Bj=0,初始 B 0 = 1 B_0=1 B0=1
关于使用伯努利数来解决等幂求和的问题,给出问题的 m m m,求出以 S m ( n ) = 1 M ( a k + 1 n k + 1 + a k n k + ⋯ + a 1 n + a 0 ) S_m(n)=\frac{1}{M}(a_{k+1}n^{k+1}+a_kn^k+\dots+a_1n+a_0) Sm(n)=M1(ak+1nk+1+aknk+⋯+a1n+a0)多项式表达的 M M M以及系数 a i a_i ai
用伯努利多项式系数推导公式计算出每项系数,在求出各个系数的分母的最小公倍数既是 M M M,其他系数除以 M M M既是 a i a_i ai
typedef long long ll;
const int maxn = 20 + 3;
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
ll lcm(ll a, ll b) {
ll ret = a / gcd(a, b) * b;
return ret ? ret : -ret;
}
struct fraction {
ll a, b;
fraction() {}
fraction(ll x) { a = x; b = 1; }
fraction(ll x, ll y) { a = x; b = y; }
void deal() {
if (b < 0) b = -b, a = -a;
ll k = gcd(a, b);
if (k < 0) k = -k;
a /= k; b /= k;
}
fraction operator +(const fraction& rhs) const {
fraction ans;
ans.b = lcm(b, rhs.b);
ans.a = ans.b / b * a + ans.b / rhs.b * rhs.a;
ans.deal();
return ans;
}
fraction operator -(const fraction& rhs) const {
fraction ans;
ans.b = lcm(b, rhs.b);
ans.a = ans.b / b * a - ans.b / rhs.b * rhs.a;
ans.deal();
return ans;
}
fraction operator *(const fraction& rhs) const {
fraction ans;
ans.a = a * rhs.a;
ans.b = b * rhs.b;
ans.deal();
return ans;
}
fraction operator /(const fraction& rhs) const {
fraction ans;
ans.a = a * rhs.b;
ans.b = b * rhs.a;
ans.deal();
return ans;
}
void println() {
printf("%lld/%lld\n", a, b);
}
};
fraction B[maxn];
ll C[maxn][maxn];
void init() {
for (int i = 1; i < maxn; ++i) {
C[i][0] = C[i][i] = 1;
for (int j = 1; j < i; ++j) {
C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
}
}
B[0] = fraction(1);
for (int i = 1; i <= 20; ++i) {
B[i] = fraction(0);
for (int j = 0; j < i; ++j)
B[i] = B[i] - fraction(C[i + 1][j]) * B[j];
B[i] = B[i] / fraction(C[i + 1][i]);
}
}
int n; fraction a[maxn];
int main() {
init();
while (~scanf("%d", &n)) {
ll Lcm = 1;
for (int i = 0; i <= n; ++i) {
a[i] = fraction(C[n + 1][i]) * B[i] * fraction(1, n + 1);
Lcm = lcm(Lcm, a[i].b);
}
printf("%lld ", Lcm);
a[1] = a[1] + fraction(1);
for (int i = 0; i <= n; ++i)
printf("%lld ", Lcm / a[i].b * a[i].a);
puts("0");
}
}
将原函数 f ( x ) f(x) f(x)映射到 f ( x + 1 ) − f ( x + b ) f(x+1)-f(x+b) f(x+1)−f(x+b)。
函数的前向差分称为函数的差分,对于函数 f ( x ) f(x) f(x),如果在等距节点 x k = x 0 + k h , ( k = 0 , 1 , … , n ) , Δ f ( x k ) = f ( x k + 1 ) − f ( x k ) x_k=x_0+kh,(k=0,1,\dots,n),\Delta f(x_k)=f(x_{k+1})-f(x_k) xk=x0+kh,(k=0,1,…,n),Δf(xk)=f(xk+1)−f(xk),则称 Δ f ( x ) \Delta f(x) Δf(x)为函数在每个小区间上的增量 y k + 1 − y k y_{k+1}-y_k yk+1−yk为 f ( x ) f(x) f(x)的一阶差分。
一阶差分的差分为二阶差分,依此类推。记 Δ n [ f ] ( x ) \Delta^n[f](x) Δn[f](x)为 f ( x ) f(x) f(x)的 n n n阶差分
若 Δ n [ f ] ( x ) = Δ { Δ n − 1 [ f ] ( x ) } = Δ n − 1 [ f ] ( x + 1 ) − Δ n − 1 [ f ] ( x ) \Delta^n[f](x)=\Delta\{\Delta^{n-1}[f](x)\}=\Delta^{n-1}[f](x+1)-\Delta^{n-1}[f](x) Δn[f](x)=Δ{Δn−1[f](x)}=Δn−1[f](x+1)−Δn−1[f](x)
根据数学归纳法,可得 Δ n [ f ] ( x ) = ∑ i = 0 n ( n i ) ( − 1 ) n − i f ( x + i ) \Delta^n[f](x)=\sum\limits_{i=0}^n{n\choose i}(-1)^{n-i}f(x+i) Δn[f](x)=i=0∑n(in)(−1)n−if(x+i),例如 Δ 2 [ f ] ( x ) = f ( x + 2 ) − 2 f ( x + 1 ) + f ( x ) \Delta^2[f](x)=f(x+2)-2f(x+1)+f(x) Δ2[f](x)=f(x+2)−2f(x+1)+f(x)
牛顿插值公式也叫做牛顿级数,由“牛顿前向差分方程”的项组成
当 x x x值间隔为单位步长为1时,有
f ( x ) = f ( a ) + x − a 1 [ Δ 1 [ f ] ( a ) + x − a − 1 2 ( Δ 2 [ f ] ( a ) + … ) ] = f ( a ) + ∑ k = 1 n Δ k [ f ] ( a ) ∏ i = 1 k [ ( x − a ) − i + 1 ] i = ∑ k = 0 n ( x − a k ) Δ k [ f ] ( a ) \begin{aligned}f(x)&=f(a)+\frac{x-a}{1}\left[\Delta^1[f](a)+\frac{x-a-1}{2}(\Delta^2[f](a)+\dots)\right]\\&=f(a)+\sum\limits_{k=1}^n\Delta^k[f](a)\prod\limits_{i=1}^k\frac{[(x-a)-i+1]}{i}\\&=\sum\limits_{k=0}^n{x-a \choose k}\Delta^k[f](a)\end{aligned} f(x)=f(a)+1x−a[Δ1[f](a)+2x−a−1(Δ2[f](a)+…)]=f(a)+k=1∑nΔk[f](a)i=1∏ki[(x−a)−i+1]=k=0∑n(kx−a)Δk[f](a)
这成立于任何多项式函数
x | Δ 0 \Delta^0 Δ0 | Δ 1 \Delta^1 Δ1 | Δ 2 \Delta^2 Δ2 | Δ 3 \Delta^3 Δ3 |
---|---|---|---|---|
1 | 1 ‾ \underline 1 1 | |||
3 ‾ \underline 3 3 | ||||
2 | 4 | 2 ‾ \underline 2 2 | ||
5 | 0 ‾ \underline 0 0 | |||
3 | 9 | 2 | ||
7 | ||||
4 | 16 |
f ( x ) = Δ 0 + Δ 1 ( x − x 0 ) 1 ! + Δ 2 ( x − x 0 ) ( x − x 0 − 1 ) 2 ! ( x 0 = 1 ) = 1 + 3 ⋅ x − 1 1 + 2 ⋅ ( x − 1 ) ( x − 2 ) 2 = 1 + 3 ( x − 1 ) + ( x − 1 ) ( x − 2 ) = x 2 \begin{aligned}f(x)&=\Delta^0+\Delta^1\frac{(x-x_0)}{1!}+\Delta^2\frac{(x-x_0)(x-x_0-1)}{2!}\,(x_0=1)\\&=1+3\cdot\frac{x-1}{1}+2\cdot\frac{(x-1)(x-2)}{2}\\&=1+3(x-1)+(x-1)(x-2)\\&=x^2\end{aligned} f(x)=Δ0+Δ11!(x−x0)+Δ22!(x−x0)(x−x0−1)(x0=1)=1+3⋅1x−1+2⋅2(x−1)(x−2)=1+3(x−1)+(x−1)(x−2)=x2
序列满足第一项为 h [ 0 ] [ 1 ] h [ 0 ] [ 2 ] h [ 0 ] [ 3 ] … h[0][1]\;h[0][2]\;h[0][3]\;\dots h[0][1]h[0][2]h[0][3]…,且满足 h [ i ] [ j ] = h [ i − 1 ] [ j + 1 ] − h [ i − 1 ] [ j ] h[i][j]=h[i-1][j+1]-h[i-1][j] h[i][j]=h[i−1][j+1]−h[i−1][j]
若差分表第一行是多项式 f ( x ) , f ( x + 1 ) , … f(x),f(x+1),\dots f(x),f(x+1),…,且多项式系数为 p p p,则满足以下性质:
p\n | 0 | 1 | 2 | 3 |
---|---|---|---|---|
0 | 0 3 0^3 03 | 1 3 1^3 13 | 2 3 2^3 23 | 3 3 3^3 33 |
1 | 1 | 7 | 19 | |
2 | 6 | 12 | ||
3 | 6 |
则计算前 n = 3 n=3 n=3项和: ∑ i = 0 3 f ( i ) = ∑ i = 0 3 ( 4 i + 1 ) h [ 0 ] [ i ] = ( 4 1 ) 0 + ( 4 2 ) 1 + ( 4 3 ) 6 + ( 4 4 ) 6 = 6 + 24 + 6 = 36 \begin{aligned}\sum\limits_{i=0}^3{f(i)}&=\sum\limits_{i=0}^3{4\choose i+1}h[0][i]\\&={4 \choose 1}0+{4 \choose 2}1+{4\choose 3}6+{4\choose 4}6\\&=6+24+6=36\end{aligned} i=0∑3f(i)=i=0∑3(i+14)h[0][i]=(14)0+(24)1+(34)6+(44)6=6+24+6=36
计算第 n = 4 n=4 n=4项, f ( 4 ) = ∑ i = 0 3 ( 3 i ) h [ 0 ] [ i ] = ( 4 0 ) 0 + ( 4 1 ) 1 + ( 4 2 ) 6 + ( 4 3 ) 6 = 64 = 4 3 \begin{aligned}f(4)&=\sum\limits_{i=0}^3{3\choose i}h[0][i]\\&={4\choose 0}0+{4\choose 1}1+{4\choose 2}6+{4\choose 3}6\\&=64=4^3\end{aligned} f(4)=i=0∑3(i3)h[0][i]=(04)0+(14)1+(24)6+(34)6=64=43
给出n和m,求 ∑ i = 1 n i m \sum\limits_{i=1}^ni^m i=1∑nim
直接套前缀和模板,计算出差分表与 C n + 1 i , i ∈ { 0 → m } C_{n+1}^i,i\in\{0\to m\} Cn+1i,i∈{0→m}
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.util.Scanner;
public class Main{
Scanner scan = new Scanner(System.in);
BigInteger c[]=new BigInteger[110];
BigInteger h[][] = new BigInteger[110][110];
void getc(BigInteger n, int m) {
c[1] = n;
for (int i = 2; i <= m+1; i++)
c[i] = c[i - 1].multiply(n.subtract(BigInteger.valueOf(i - 1)))
.divide(BigInteger.valueOf(i));
}
PrintWriter out=new PrintWriter(new OutputStreamWriter(System.out));
void run() {
int cas=scan.nextInt();
while(cas-->0){
BigInteger n =scan.nextBigInteger().add(BigInteger.ONE);
int m = scan.nextInt();
for (int i = 0; i <= m; i++)
h[0][i] = BigInteger.valueOf(i).pow(m);
for (int i = 1; i <= m; i++)
for (int j = 0; j <= m - i; j++)
h[i][j] = h[i - 1][j + 1].subtract(h[i - 1][j]);
BigInteger ans = BigInteger.ZERO;
getc(n, m);
for (int i = 0; i <= m; i++)
ans=ans.add(h[i][0].multiply(c[i+1]));
out.println(ans);
out.flush();
}
}
public static void main(String[] args) {
new Main().run();
}
}