数学相关

线性筛素数

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) ababmodφ(n)(modn);可以用来对于求幂运算时缩小数据范围和计算次数;特别的若a和n不互质,则 a b ≡ a b m o d φ ( n ) + φ ( n ) ( m o d n ) a^b≡a^{bmodφ(n)+φ(n)}(modn) ababmodφ(n)+φ(n)(modn)

引理

对于任意互质的正整数a和n,满足 a x ≡ 1 ( m o d n ) a^x≡1(modn) ax1(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=0s(1pi1),其中 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) apa(modp) ; 属于欧拉定理的特例

Miller_Rabin素数判断

  1. 利用费马小定理,对数n,若它是素数,就满足 x n − 1 ≡ 1 ( m o d n ) x^{n-1}≡1(modn) xn11(modn),我们随机几个数判断若出现结果不为1则可以判断不为素数
  2. 二次检测, n − 1 = r ∗ 2 d n-1=r*2^d n1=r2d,依次检验 r ∗ x d − 1 m o d n r*x^{d-1}modn rxd1modn的结果若为1,则只能 x = 1 , x = n − 1 x=1,x=n-1 x=1,x=n1,否则不为素数
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) axb(modm),转换为 a x + m y = b ax+my=b ax+my=b,使用 e x g c d exgcd exgcd求解,注意有多解

  1. 对于 a x + m y = b ax+my=b ax+my=b的有整数解的充分条件是 b ( m o d g c d ( a , m ) ) = 0 b\pmod{gcd(a, m)}=0 b(modgcd(a,m))=0
  2. 对于 x 0 , y 0 x_0,y_0 x0,y0是原式的一组解,则其他解可由 x = x 0 + b ∗ t / g c d ( a , m ) x=x_0+b*t/gcd(a, m) x=x0+bt/gcd(a,m)求解
  3. d = g c d ( a , m ) d=gcd(a,m) d=gcd(a,m),则方程在 [ 0 , m / d − 1 ] [0,m/d-1] [0,m/d1]上有唯一解
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;
}

CRT

解决一元线性同余方程 x ≡ a i ( m o d m i ) x≡a_i(mod m_i) xai(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)$的一个解
  1. 那么x有整数解,为 x = ∏ i = 1 n a i ∗ M i ∗ t i x=\prod_{i=1}^n{a_i*M_i*t_i} x=i=1naiMiti x为一个特解,通解可以用 x + k m ( k ∈ Z ) x+km(k \in Z) x+km(kZ)表示,最小整数解为 x   m o d   m x\bmod m xmodm
/*
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;
}

exCRT

使用与 m i m_i mi不为互质的一元线性同余方程组

  1. 考虑合并 { x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) \begin{cases} x≡a_1(mod m_1)\\x≡a_2(modm_2)\end{cases} {xa1(modm1)xa2(modm2)得到 { x ≡ a 1 + m 1 y 1 x ≡ a 2 + m 2 y 2 \begin{cases} x≡a_1+m_1y_1\\x≡a_2+m_2y_2\end{cases} {xa1+m1y1xa2+m2y2也即是 m 1 y 1 + m 2 y 2 = a 2 − a 1 m_1y_1+m_2y_2=a_2-a_1 m1y1+m2y2=a2a1
  2. 使用 e x g c d exgcd exgcd解去一组可行解 ( x 1 , y 1 ) (x1,y1) (x1,y1),带回去计算出关于x的可行解 x 1 x_1 x1,则 x ≡ x 1 ( m o d m ) x≡x_1(mod m) xx1(modm),其中 m = l c m ( m 1 , m 2 ) m=lcm(m_1, m_2) m=lcm(m1,m2)
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) x2n(modp),给出n和p,是否存在一个式子满足该式子,即模p意义下的开根 n \sqrt n n 参考链接

  1. 钱德勒Legender符号 ( a p ) = { 1 , a 在 模 p 意 义 是 二 次 剩 余 − 1 , a 在 模 p 意 义 下 非 二 次 剩 余 0 , a ≡ 0 ( m o d p ) ({\frac{a}{p}})=\begin{cases}1,a在模p意义是二次剩余\\-1,a在模p意义下非二次剩余\\0,a≡0(modp) \end{cases} (pa)=1ap1ap0a0(modp)
  2. ( a p ) ≡ a p − 1 2   m o d   p ({\frac{a}{p}})≡a^{{\frac{p-1}{2}}}\bmod p (pa)a2p1modp
  3. 设a是满足 w ≡ a 2 − n w≡a^2-n wa2n是模p的非二次剩余,即 x 2 ≡ w ( m o d p ) x^2≡w(modp) x2w(modp)无解,那么 x ≡ ( a + w ) p + 1 2 x≡(a+\sqrt w)^{\frac{p+1}{2}} x(a+w )2p+1是二次同余方程 x 2 ≡ n   m o d   p x^2≡n\bmod p x2nmodp的解
#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) ag1(modp)的最小的 g g g正好等于 φ ( p ) φ(p) φ(p)

  1. p − 1 p-1 p1进行质因子分解得到不同的质因子 d 1 , d 2 , . . . , d m d1,d2,...,dm d1,d2,...,dm
  2. 对任意 1 < a < p 11<a<p,只需要检验每个质因子 a p − 1 d i a^{\frac{p-1}{d_i}} adip1这m个数中是否存在模p意义下与1同余,则a不是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;
}

BSGS离散对数

关于 a x ≡ b ( m o d p ) a^x≡b (modp) axb(modp)的求解

  1. x = k m − t x=km-t x=kmt,其中 m = p m=\sqrt{p} m=p ,则原式为 a k m − t ≡ b ( m o d p ) , a k m ≡ b a t ( m o d p ) a^{km-t}≡b(modp),a^{km}≡ba^t(modp) akmtb(modp)akmbat(modp)
  2. 枚举 k , t < = m k, t<=m k,t<=m,计算 a k m a^{km} akm并记录,再计算 b a t ba^t bat检查是否已经存在
  3. 带入 x = k m − t x=km-t x=kmt
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;
	}
}

计算 x k ≡ a ( m o d n ) x^k≡a (modn) xka(modn)

  1. 根据原根性质,找出 b b b的原根 g g g
  2. 则令 x = g c x=g^c x=gc x k ≡ a   m o d   p → ( g k ) c ≡ a   m o d   p x^k≡a\bmod p\rightarrow(g^k)^c≡a\bmod p xkamodp(gk)camodp,即上面一种形式
  3. 使用上面解法得出 c c c的特解,则通解为 x ≡ g c + i ∗ n − 1 g c d ( n − 1 , k ) x≡g^{c+i*{\frac{n-1}{gcd(n-1,k)}}} xgc+igcd(n1,k)n1
    证明过程
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);
}

Ramsey定理

在人数为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(a1,b)+N(a,b1)N(a1,b)N(a,b1)N(a,b)N(a1,b)+N(a,b1)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,

性质

  1. 对于任意正整数n, ∑ d ∣ n μ ( d ) = [ n = = 1 ] \sum_{d|n}\mu(d)=[n==1] dnμ(d)=[n==1]
  2. 对于任意正整数n, ∑ d ∣ n μ ( d ) d = ϕ ( n ) n \sum_{d|n}{\frac{\mu(d)}{d}}={\frac{\phi(n)}{n}} dndμ(d)=nϕ(n)

两个反演形式

  1. g ( n ) = ∑ d ∣ n f ( d ) g(n)=\sum_{d|n}f(d) g(n)=dnf(d)得到 f ( n ) = ∑ d ∣ n μ ( d ) g ( n d ) f(n)=\sum_{d|n}\mu(d)g({\frac{n}{d}}) f(n)=dnμ(d)g(dn),因子关系
  2. g ( n ) = ∑ n ∣ d f ( d ) g(n)=\sum_{n|d}f(d) g(n)=ndf(d)得到 f ( n ) = ∑ n ∣ d μ ( d n ) g ( d ) f(n)=\sum_{n|d}\mu({\frac{d}{n}})g(d) f(n)=ndμ(nd)g(d),倍数关系

迪利克雷卷积

满足 ( f ∗ g ) ( n ) = ∑ d ∣ n f ( d ) ∗ g ( n d ) (f*g)(n)=\sum_{d|n}f(d)*g({\frac{n}{d}}) (fg)(n)=dnf(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)=dn1σ(n)σ(n)=dnd

完全积性函数

{ ϵ ( 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)=1id(n)=n

迪利克雷卷积性质

  1. f ∗ g = g ∗ f f*g=g*f fg=gf,交换律
  2. ( ( f ∗ g ) ∗ h ) = ( f ∗ ( g ∗ h ) ) ((f*g)*h)=(f*(g*h)) ((fg)h)=(f(gh)),结合律
  3. ( ( f + g ) ∗ h ) = f ∗ h + g ∗ h ((f+g)*h)=f*h+g*h ((f+g)h)=fh+gh,分配律

常用等式

  1. μ ∗ I = ϵ \mu*I=\epsilon μI=ϵ ∑ d ∣ n μ ( d ) = [ n = = 1 ] \sum_{d|n}\mu(d)=[n==1] dnμ(d)=[n==1]
  2. ϕ ∗ I = i d \phi*I=id ϕI=id ∑ d ∣ n ϕ ( d ) = n \sum_{d|n}\phi(d)=n dnϕ(d)=n
  3. i d ∗ μ = ϕ id*\mu=\phi idμ=ϕ ∑ d ∣ n μ ( d ) n d = ϕ \sum_{d|n}\mu(d){\frac{n}{d}}=\phi dnμ(d)dn=ϕ,欧拉函数定义

例题

求所有由a~z组成的长度为n的字符串中,没有周期性的字符串数量。重复性是指一个长度为n的字符串可以由一个长度为m n m \frac{n}{m} mn次得到, 2 < = n < = 1 0 9 2<=n<=10^9 2<=n<=109

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)=mng(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)=dnf(dn)μ(d)=dn26dnμ(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 = f ∗ g h=f*g h=fg,求 ∑ i = 1 n h ( i ) \sum_{i=1}^nh(i) i=1nh(i)
  • S ( n ) = ∑ i = 1 n f ( i ) S(n)=\sum_{i=1}^nf(i) S(n)=i=1nf(i)
  • ∑ i = 1 n h ( i ) = ∑ i = 1 n ∑ d ∣ i g ( d ) f ( i d ) = ∑ d = 1 n g ( d ) ∗ ∑ i = 1 n d f ( i ) \sum_{i=1}^nh(i)=\sum_{i=1}^n\sum_{d|i}g(d)f({\frac{i}{d}})=\sum_{d=1}^ng(d)*\sum_{i=1}^{\frac{n}{d}}f(i) i=1nh(i)=i=1ndig(d)f(di)=d=1ng(d)i=1dnf(i)
  • ∑ i = 1 n h ( i ) = ∑ d = 1 n g ( d ) ∗ S ( n d ) \sum_{i=1}^nh(i)=\sum_{d=1}^ng(d)*S({\frac{n}{d}}) i=1nh(i)=d=1ng(d)S(dn)
  • 将右边等式的第一项提出来得到 ∑ i = 1 n h ( i ) = g ( 1 ) S ( n ) + ∑ d = 2 n g ( d ) ∗ S ( n d ) \sum_{i=1}^nh(i)=g(1)S(n)+\sum_{d=2}^ng(d)*S({\frac{n}{d}}) i=1nh(i)=g(1)S(n)+d=2ng(d)S(dn),移项 g ( 1 ) S ( n ) = ∑ i = 1 n h ( i ) − ∑ d = 2 n g ( d ) S ( n d ) g(1)S(n)=\sum_{i=1}^nh(i)-\sum_{d=2}^ng(d)S({\frac{n}{d}}) g(1)S(n)=i=1nh(i)d=2ng(d)S(dn)

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)=1i=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=1nid=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}}) fg=dndϕ(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 dndϕ(d)dn=dnϕ(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=1ni2d=2ndS(dn)

总结:首先筛出数据范围根号内的积性函数的前缀后,再递归实现杜教筛,使用unodered_map减少复杂度

线性求逆元

t = P i , k = P % I t=\frac{P}{i},k=P\%I t=iPk=P%I
对于 t ∗ i + k ≡ 0   m o d   P t*i+k≡0\bmod P ti+k0modP,等式同除 i ∗ k i*k ik
得到 t k + 1 i ≡ 0   m o d   P {t\over k}+{1\over i}≡0\bmod P kt+i10modP,即 t ∗ i n v [ k ] + i n v [ i ] ≡ 0   m o d   P t*inv[k]+inv[i]≡0\bmod P tinv[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]=(PiP)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 n2两个质数;否则可以表示为偶数 ( n − 3 ) (n-3) (n3)和质数 3 3 3,就是 3 3 3个质数的和

Polya定理

解决等价类计数问题,等价关系即满足等价关系的两个元素,还满足自反性、对称性以及传递性。统计等价类的个数:使用置换集合F来描述,如果一个置换将一个方案映射到了另一个方案,说明两者是等价的。
Burnside定理:如果一个方案s经过一个置换f后不变,则称s为不动点,将f的所有不动点数目记为c(f),那么等价类数目等于所有置换的c(f)的平均值。
典型的等价类置换:背景都是由n个珠子构成的环,并且颜色总数为t

  1. 旋转
    如果旋转i颗珠子的间距,那么共有 g c d ( i , n ) gcd(i, n) gcd(i,n)个循环,所以不动点的总数为 a = t g c d ( 0 , n ) + t g c d ( 1 , n ) + . . . + t g c d ( n − 1 , n a=t^{gcd(0,n)}+t^{gcd(1,n)}+...+t^{gcd(n-1,n} a=tgcd(0,n)+tgcd(1,n)+...+tgcd(n1,n
  2. 翻转
    分为奇偶情况:奇数时,对称轴有n条,每一条形成 n + 1 2 \frac{n+1}{2} 2n+1个循环,所以不动点的总数为 b = n ∗ t n + 1 2 b=n*t^\frac{n+1}{2} b=nt2n+1
    偶数时,穿过珠子的对称轴有 n 2 \frac{n}{2} 2n条,每一条都形成 n 2 + 1 \frac{n}{2}+1 2n+1个循环,不穿过珠子的对称轴有 n 2 \frac{n}{2} 2n,每一条都形成 n 2 \frac{n}{2} 2n个循环。所以不动点的总数为 b = n 2 ∗ ( t n 2 + 1 + t n 2 b=\frac{n}{2}*(t^{\frac{n}{2}+1}+t^\frac{n}{2} b=2n(t2n+1+t2n
    最后,根据polya定理,只具有旋转性质的等价类有 a n \frac{a}{n} na种,只具有翻转性质的有 b n \frac{b}{n} nb,两张都具有有 a + b 2 n \frac{a+b}{2n} 2na+b

例题

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=dt。则 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(dt,n)=dgcd(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} ndnmdϕ(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);
}

求出 n ! n! n!最后的非0位

比如我们需要求出 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%10x)+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=1B1=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+11k=0m(km+1)Bknm+1k

推导公式

∑ 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+1yk 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)=Δ{Δn1[f](x)}=Δn1[f](x+1)Δn1[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=0n(in)(1)nif(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)

差分的性质

  • 若C为常数,则 Δ C = 0 \Delta C=0 ΔC=0
  • 线性:如果a和b为常数,则有 Δ ( a f + b g ) = a Δ f + b Δ g \Delta(af+bg)=a\Delta f+b\Delta g Δ(af+bg)=aΔf+bΔg
  • 乘法定则: Δ ( f g ) = f Δ g + g Δ f + Δ f Δ g \Delta(fg)=f\Delta g+g\Delta f+\Delta f\Delta g Δ(fg)=fΔg+gΔf+ΔfΔg
  • 级数: ∑ n = a b Δ f ( n ) = f ( b + 1 ) − f ( a ) \sum\limits_{n=a}^b{\Delta f(n)}=f(b+1)-f(a) n=abΔf(n)=f(b+1)f(a)

牛顿级数

牛顿插值公式也叫做牛顿级数,由“牛顿前向差分方程”的项组成

单位步长情况

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)+1xa[Δ1[f](a)+2xa1(Δ2[f](a)+)]=f(a)+k=1nΔk[f](a)i=1ki[(xa)i+1]=k=0n(kxa)Δ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!(xx0)+Δ22!(xx0)(xx01)(x0=1)=1+31x1+22(x1)(x2)=1+3(x1)+(x1)(x2)=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[i1][j+1]h[i1][j]

若差分表第一行是多项式 f ( x ) , f ( x + 1 ) , … f(x),f(x+1),\dots f(x),f(x+1),,且多项式系数为 p p p,则满足以下性质:

  • 差分表 n + 1 n+1 n+1行都为0,第 n n n行为一个常数
  • 序列的一般表达式 f ( n ) = ∑ i = 0 p ( n i ) h [ 0 ] [ i ] , n ∈ Z + f(n)=\sum\limits_{i=0}^p{n\choose i}h[0][i],n\in Z^+ f(n)=i=0p(in)h[0][i],nZ+,参考牛顿级数
  • 前缀和 ∑ i = 0 n f ( i ) = ∑ i = 0 p ( n + 1 i + 1 ) h [ 0 ] [ i ] , n ∈ Z + \sum\limits_{i=0}^nf(i)=\sum\limits_{i=0}^p{n+1\choose i+1}h[0][i],n\in Z^+ i=0nf(i)=i=0p(i+1n+1)h[0][i],nZ+

例子

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=03f(i)=i=03(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=03(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=1nim

直接套前缀和模板,计算出差分表与 C n + 1 i , i ∈ { 0 → m } C_{n+1}^i,i\in\{0\to m\} Cn+1i,i{0m}

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();
	}
}

你可能感兴趣的:(总结)