• 洲阁筛 & min_25筛学习笔记


    洲阁筛

    给定一个积性函数$F(n)$,求$sum_{i = 1}^{n}F(n)$。并且$F(n)$满足在素数和素数次幂的时候易于计算。

    显然有:

    $sum_{i = 1}^{n} F(n) = sum_{i = 1}^{sqrt{n}}F(i) left(sum_{sqrt{n} < pleqslant n/i, p is a prime} F(p) ight) + sum_{i = 1, i has no prime factor greater than sqrt{n}}^{n} F(i)$。

    Part 1

    第一部分是要求出$sum_{sqrt{n}<p leqslant n/i, p is a prime} F(p)$

    假设小于等于$sqrt{n}$的素数从小到大分别是$P_1, P_2, cdots, P_{m}$。

    设$f_{i, j}$表示$[1, j]$中和前$i$个素数互质的数的函数值和。

    当$i = 0$时候想办法计算。

    考虑当$i > 0$的时候的转移。考虑从$f_{i - 1, j}$中需要删掉最小质因子等于$P_i$的数的贡献。

    所以有转移式子$f_{i, j} = f_{i - 1, j} - F(P_i) f_{i - 1, left lfloor j / P_i ight floor}$

    如果$F(n)$本身不是完全积性函数,需要把它拆成若干积性函数的和。注意这一部分只用保证素数处取值相同即可。

    注意到第一维约有$Oleft (frac{sqrt{n}}{log n} ight)$种取值,第二维有$Oleft (sqrt{n} ight)$种取值。暴力计算的复杂度是$Oleft (frac{n}{log n} ight )$

    优化

    考虑当$j < P_{i+ 1}$的时候,$f_{i, j} = 1$。考虑把条件放缩成$j < P_i$。

    因此,当$P_i leqslant j < P_{i}^2$的时候$f_{i, j} = f_{i - 1, j} - F(P_i)$。

    所以做法是:

    • 维护一个$F(P_i)$的前缀和
    • 考虑转移时的$f_{i - 1, left lfloor j / P_i ight floor}$
      • 当$j geqslant P_{i}^2$的时候暴力计算
      • 当$P_i leqslant j < P_{i}^2$的时候,需要这样的状态的时候减去一段和就行了。
      • 当$j < P_i$的时候,由于$f_{i, 0} = 0$,所以这一部分的值不会改变。

    所以对于每个$j$,有效的状态只有$Oleft (frac{sqrt{j}}{log j} ight )$。

    时间复杂度为:$sum_{i = 1}^{sqrt{n}} O left (frac{sqrt{i}}{log i} ight ) + sum_{i = 1}^{sqrt{n}} Oleft ( frac{sqrt{n/i}}{log {n/i}} ight)$

    由于第一部分显然小于第二部分,所以只用考虑第二部分的和。

    所以有:$sum_{i = 1}^{sqrt{n}}O left (frac{sqrt{n/i}}{log n - log i} ight)$

    由于$log i leqslant log sqrt{n} = frac{1}{2}log n$

    所以:$sum_{i = 1}^{sqrt{n}}O left (frac{sqrt{n/i}}{frac{1}{2}log n} ight) = sum_{i = 1}^{sqrt{n}}Oleft (frac{sqrt{n / i}}{log n} ight) = Oleft ( frac{n^{3/4}}{log n} ight)$

    Part 2

    这一部分是要求出$sum_{i = 1, i has no prime factor greater than sqrt{n}}^{n} F(i)$

    假设小于等于$sqrt{n}$的素数从小到大分别是$P_1, P_2, cdots, P_{m}$。

    设$g_{i, j}$表示在$[1, j]$中,质因子只包含$P_{i}, P_{i + 1}, cdots, P_{m}$的数的函数值的和。

    转移考虑枚举当前质数$P_i$的指数。所以有:$g_{i, j} = g_{i + 1, j} + sum_{e = 1}^{log_{P_i} j} F(P_{i}^{e}) g_{i + 1, lfloor j / P_i^e floor}​$

    对于时间复杂度,考虑对于每一个$j$的转移的枚举量,可以得到大概是$O(frac{sqrt{j}}{log j})$。(不会积分,告辞)

    和Part 1一样,得到暴力计算这一部分的复杂度是$O(frac{n}{log n})$。

    优化

    因为质因子是从大到小枚举的,所以当$j < P_{i}$的时候,$g_{i, j} = 1$。

    所以当满足$P_i leqslant j < P_{i}^2$,满足$g_{i, j} = g_{i + 1, j} + F(P_i)$。

    和Part 1一样,分成三段,一段暴力转移,一段需要时前缀和,一段不需要维护。

    复杂度也是同样的计算方法

      1 /**
      2  * SPOJ
      3  * Problem#DIVCNT3
      4  * Accepted
      5  * Time: 35190ms
      6  * Memory: 33792k
      7  */
      8 #include <bits/stdc++.h>
      9 using namespace std;
     10 typedef bool boolean;
     11 
     12 template <typename T>
     13 void pfill(T* pst, const T* ped, T val) {
     14     for ( ; pst != ped; *(pst++) = val);
     15 }
     16 
     17 #define ll long long
     18 
     19 typedef class Euler {
     20     public:
     21         int num;
     22         boolean *vis;
     23         int *pri, *d3, *mp;
     24         
     25         Euler(int n) : num(0) {
     26             d3 = new int[(n + 1)];
     27             mp = new int[(n + 1)];
     28             pri = new int[(n + 1)];
     29             vis = new boolean[(n + 1)];
     30             pfill(vis, vis + n + 1, false);
     31             d3[1] = 1;
     32             for (int i = 2; i <= n; i++) {
     33                 if (!vis[i]) {
     34                     d3[i] = 4, pri[num++] = i, mp[i] = 1;
     35                 }
     36                 for (int j = 0, _ = n / i, x; j < num && pri[j] <= _; j++) {
     37                     vis[x = pri[j] * i] = true;
     38                     if (!(i % pri[j])) {
     39                         d3[x] = (d3[i / mp[i]] + 3) * d3[mp[i]];
     40                         mp[x] = mp[i];
     41                         break;
     42                     } else {
     43                         mp[x] = i, d3[x] = d3[i] << 2;
     44                     }
     45                 } 
     46             }
     47         }
     48         ~Euler() {
     49             delete[] mp;
     50             delete[] vis;
     51             delete[] d3;
     52             delete[] pri;
     53         }
     54 } Euler;
     55 
     56 const int N = 330000;
     57 
     58 Euler *_euler;
     59 
     60 int T;
     61 vector<ll> ns;
     62 
     63 ll maxn, D;
     64 
     65 int cnt_prime;
     66 int *pri, *d3;
     67 
     68 inline void init() {
     69     scanf("%d", &T);
     70     for (ll x; T--; ) {
     71         scanf("%lld", &x);
     72         ns.push_back(x);
     73         maxn = max(maxn, x);
     74     }
     75     D = sqrt(maxn + 0.5) + 1;
     76     while (D * 1ll * D <= maxn)
     77         D++;
     78     _euler = new Euler(D + 23);
     79     cnt_prime = _euler->num;
     80     pri = _euler->pri, d3 = _euler->d3;
     81 }
     82 
     83 ll n;
     84 int cnt_P[N];
     85 int range0[N], range1[N];
     86 ll f0[N], f1[N], g0[N], g1[N];
     87 
     88 void precalc() {
     89     for (int i = 1; i < D; i++) {
     90         range0[i] = range0[i - 1];
     91         while (pri[range0[i]] * 1ll * pri[range0[i]] <= i) {
     92             range0[i]++;
     93         }
     94     }
     95     for (int i = D - 1; i; i--) {
     96         ll y = n / i;
     97         range1[i] = range1[i + 1];
     98         while (pri[range1[i]] * 1ll * pri[range1[i]] <= y) {
     99             range1[i]++;
    100         }
    101     }
    102     pfill(cnt_P, cnt_P + D, 0);
    103     for (int i = 0; pri[i] < D; i++) {
    104         cnt_P[pri[i]]++;
    105     }
    106     for (int i = 1; i < D; i++) {
    107         cnt_P[i] += cnt_P[i - 1];
    108     }
    109 }
    110 
    111 int nump;
    112 ll get_value_f(int i, ll j) {
    113     if (j >= D) {
    114         int rj = n / j;
    115         return f1[rj] + (max(0, nump - max(range1[rj], i)) << 2);
    116     }
    117     return f0[j] + (max(0, cnt_P[j] - max(range0[j], i)) << 2);
    118 }
    119 
    120 void calculate_f() {
    121     for (nump = 0; pri[nump] < D; nump++);
    122     for (int i = 1; i < D; i++) {
    123         f0[i] = 1, f1[i] = 1;
    124     }
    125     for (int i = nump - 1; ~i; i--) {
    126         for (int j = 1; j < D && i < range1[j]; j++) {
    127             ll m = n / j, coef = 1;
    128             while (m /= pri[i]) {
    129                 f1[j] += (coef += 3) * get_value_f(i + 1, m);
    130             }
    131         }
    132         for (int j = D - 1; j && i < range0[j]; j--) {
    133             ll m = j, coef = 1;
    134             while (m /= pri[i]) {
    135                 f0[j] += (coef += 3) * get_value_f(i + 1, m);
    136             }
    137         }
    138     }
    139     for (int i = 1; i < D; i++) {
    140         f1[i] = get_value_f(0, n / i);        
    141     }
    142     for (int i = 1; i < D; i++) {
    143         f0[i] = get_value_f(0, i);
    144     }
    145 }
    146 
    147 ll get_value_g(int i, ll j) {
    148     if (j >= D) {
    149         int rj = n / j;
    150         return g1[rj] - max(0, i - range1[rj]);
    151     }
    152     return g0[j] - max(0, min(i, cnt_P[j]) - range0[j]);
    153 }
    154 
    155 void calculate_g() {
    156     for (int i = 1; i < D; i++) {
    157         g0[i] = i, g1[i] = n / i;
    158     }
    159     int cp = 0;
    160     for (int i = 0; pri[i] < D; i++, cp++) {
    161         for (int j = 1; j < D && i < range1[j]; j++) {
    162             g1[j] -= get_value_g(i, n / j / pri[i]);
    163         }
    164         for (int j = D - 1; j && i < range0[j]; j--) {
    165             g0[j] -= get_value_g(i, j / pri[i]);
    166         }
    167     }
    168     for (int i = 1; i < D; i++) {
    169         (g1[i] = get_value_g(cp, n / i) - 1) <<= 2;
    170     }
    171     for (int i = 1; i < D; i++) {
    172         (g0[i] = get_value_g(cp, i) - 1) <<= 2;
    173     }
    174 }
    175 
    176 void Solve(ll _n) {
    177     n = _n;
    178     D = sqrt(n + 0.5) + 1;
    179     while (D * 1ll * D <= n)
    180         D++;
    181     precalc();
    182     calculate_g();
    183     calculate_f();
    184     ll ans = f1[1];
    185     for (int i = 1; i < D; i++) {
    186         ans += d3[i] * g1[i];
    187 //        cerr << d3[i] << " " << g1[i] << '
    '; 
    188     }
    189     printf("%lld
    ", ans);
    190 }
    191 
    192 inline void solve() {
    193     for (int i = 0; i < (signed) ns.size(); i++) {
    194         Solve(ns[i]);
    195     }
    196 }
    197 
    198 int main() {
    199     init();
    200     solve();
    201     return 0;
    202 }
    洲阁筛

    Min25筛

    Part 1

    假设小于等于$sqrt{n}$的素数从小到大分别是$P_1, P_2, cdots, P_{m}$。

    设$g_{i, j} = sum_{k = 2}^{j} [k的最小质因子大于P_i或者k是质数] F(k)$

    转移式子:

    $$g_{i, j} =
    egin{cases}g_{i - 1, j} - F(P_i)(g_{i - 1, lfloor j / P_i floor} - g_{i - 1, P_i - 1}) & (j geqslant P_i^2) \ 
    g_{i - 1, j} & (1 leqslant j < P_i^2)
    end{cases}
    $$

    和洲阁筛类似,不过第一种情况还需要抠掉素数的贡献。

    这里也要求$F$是一个完全积性函数。

    我们考虑求出$1, 2, cdots, sqrt{n}, lfloor n / sqrt{n} floor, cdots, lfloor n / 2 floor, n$处的值。

    Part 2

    这里考虑计算$S(n, i) = sum_{i = 2}^{n}[n的最小质因子大于等于P_i]F(n)$。

    那么显然答案等于$S(n, 1) + F(1)$。

    这里简单粗暴一点,如果是素数,那么可以用第一部分计算的答案再减去某个前缀和得到,否则枚举这个合数的最小质因子。不难得到这样一个式子:

    $$
    S(n, i) = g_{m, n} - sum_{j = 1}^{i - 1}F(P_j) + sum_{j = i wedge P_j^2 leqslant n}sum_{e = 1wedge P_{j}^{e + 1}leqslant n} F(P_{j}^{e}) S(lfloor n/P_{j}^e floor, j + 1) + F(P_{j}^{e + 1})
    $$

    前一半是计算素数的贡献。

    考虑计算合数的贡献,后一半是先枚举最小质因子,因为它是一个合数最小质因子,所以$p^2 leqslant n$

    $p^{e + 1} leqslant n$是类似的原因。

    时间复杂度被证明为是$O(frac{n}{Poly(log n)})$。

    (似乎目前min_25在$10^{13}$范围内吊打其他筛)

    /**
     * loj
     * Problem#6053
     * Accepted
     * Time: 332ms 
     * Memory: 2684k
     */
    #include <bits/stdc++.h>
    using namespace std;
    typedef bool boolean;
    
    #define ll long long
    
    void exgcd(int a, int b, int& x, int& y) {
    	if (!b) {
    		x = 1, y = 0;
    	} else {
    		exgcd(b, a % b, y, x);
    		y -= (a / b) * x;
    	}
    }
    
    int inv(int a, int n) {
    	int x, y;
    	exgcd(a, n, x, y);
    	return (x < 0) ? (x + n) : (x);
    }
    
    const int Mod = 1e9 + 7;
    
    template <const int Mod = :: Mod>
    class Z {
    	public:
    		int v;
    
    		Z() : v(0) {	}
    		Z(int x) : v(x){	}
    		Z(ll x) : v(x % Mod) {	}
    
    		friend Z operator + (const Z& a, const Z& b) {
    			int x;
    			return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
    		}
    		friend Z operator - (const Z& a, const Z& b) {
    			int x;
    			return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
    		}
    		friend Z operator * (const Z& a, const Z& b) {
    			return Z(a.v * 1ll * b.v);
    		}
    		friend Z operator ~(const Z& a) {
    			return inv(a.v, Mod);
    		}
    		friend Z operator - (const Z& a) {
    			return Z(0) - a;
    		}
    		Z& operator += (Z b) {
    			return *this = *this + b;
    		}
    		Z& operator -= (Z b) {
    			return *this = *this - b;
    		}
    		Z& operator *= (Z b) {
    			return *this = *this * b;
    		}
    		friend boolean operator == (const Z& a, const Z& b) {
    			return a.v == b.v;
    		} 
    };
    
    Z<> qpow(Z<> a, int p) {
    	Z<> rt = Z<>(1), pa = a;
    	for ( ; p; p >>= 1, pa = pa * pa) {
    		if (p & 1) {
    			rt = rt * pa;
    		}
    	}
    	return rt;
    }
    
    typedef Z<> Zi;
    
    const Zi inv2 ((Mod + 1) >>1);
    
    const int Dmx = 1e5 + 5;
    
    ll n;
    int D;
    int pnum;
    int pri[Dmx], sf[Dmx];
    
    void Euler(int n) {
    	static bitset<Dmx + 23> vis;
    	for (int i = 2; i <= n; i++) {
    		if (!vis.test(i)) {
    			pri[pnum++] = i;
    		}
    		for (int *p = pri, *_ = pri + pnum, x; p != _ && (x = *p * i) <= n; p++) {
    			vis.set(x);
    			if (!(i % *p)) {
    				break;
    			}
    		}
    	}
    	for (int i = 1; i <= pnum; i++) {
    		sf[i] = sf[i - 1] + (pri[i - 1] ^ 1);
    	}
    }
    
    template <typename T>
    class SieveArray {
    	public:
    		ll n;
    		vector<T> a0;
    		vector<T> a1;
    		
    		void init(ll n, int D) {
    			this->n = n;
    			a0.assign(D, 0);
    			a1.assign(D, 0);
    		}
    		
    		T& operator [] (ll p) {
    			return (p >= (signed) a0.size()) ? a1[n / p] : a0[p]; 
    		}
    };
    
    SieveArray<int> range;
    SieveArray<Zi> f0, f1;
    
    Zi S(ll n, int m) {
    	if (pri[m] > n) {
    		return 0;
    	}
    	Zi rt = f1[n] - sf[m];
    	for (int j = m; j < pnum && 1ll * pri[j] * pri[j] <= n; j++) {
    		int p = pri[j];
    		ll nn = n, p2 = 1ll * p * p;
    		for (int e = 1; p2 <= nn; e++) {
    			rt += S(nn /= p , j + 1) * (p ^ e) + (p ^ (e + 1));
    		}
    	}
    	return rt;
    }
    
    int main() {
    	scanf("%lld", &n);
    	D = sqrt(D + 0.5) + 1;
    	while (1ll * D * D <= n)
    		D++;
    	Euler(D + 3);
    	range.init(n, D);
    	f0.init(n, D);
    	f1.init(n, D);
    	ll t = 0;
    	range[0] = 0;
    	for (int i = 1; i < D; i++) {
    		while (t * t <= i)
    			t++;
    		range.a0[i] = t;
    	}
    	t = 0;
    	for (int i = D; --i; ) {
    		ll v = n / i;
    		while (t * t <= v)
    			t++;
    		range.a1[i] = t;
    	}
    	for (int i = 1; i < D; i++) {
    		f0.a0[i] = i - 1;
    		f1.a0[i] = ((Zi(i) * (i + 1)) * inv2) - 1;
    		ll v = n / i;
    		f0.a1[i] = v - 1;
    		f1.a1[i] = ((Zi(v) * (v + 1)) * inv2) - 1;
    	}
    	for (int i = 0; i < pnum; i++) {
    		int p = pri[i];
    		if (p >= D) {
    			break;
    		}
    		ll v;
    		for (int j = 1; j < D && p < range.a1[j]; j++) {
    			v = n / (1ll * j * p);
    			f0.a1[j] -= f0[v] - f0.a0[p - 1];
    			f1.a1[j] -= (f1[v] - f1.a0[p - 1]) * p;
    		}
    		for (int j = D; p < range.a0[--j]; ) {
    			f0.a0[j] -= f0.a0[j / p] - f0.a0[p - 1];
    			f1.a0[j] -= (f1.a0[j / p] - f1.a0[p - 1]) * p;
    		}
    	}
    	for (int i = 1; i < D; i++) {
    		f1.a0[i] -= f0.a0[i];
    		f1.a0[i] += (i >= 2) * 2;
    		f1.a1[i] -= f0.a1[i];
    		f1.a1[i] += (2 * i <= n) * 2;
    	}
    	Zi ans = S(n, 0) + 1;
    	printf("%d
    ", ans.v);
    	return 0;
    }
  • 相关阅读:
    软件测试工具
    nat 转发
    修改Oracle 10g Express Edition的字符集
    java数字证书学习笔记
    【Java–XML】JDOM解析XML字符串(非XML文档)
    JAVA Web快速开发部署(Javarebel实现真正高效的tomcat热部署)
    热天稀饭配方
    javascript 使用正则实现replaceall功能
    设置eclipse中各类型文件的默认浏览器(如设置flex的.mxml的编辑器为MXML Editor)
    GAE中JDO数据清除
  • 原文地址:https://www.cnblogs.com/yyf0309/p/10424858.html
Copyright © 2020-2023  润新知