#P2920. 第2题-Tk的素区间
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 39
            Accepted: 6
            Difficulty: 5
            
          
          
          
                       所属公司 : 
                              阿里
                                
            
                        
              时间 :2025年4月28日-阿里国际(算法岗)
                              
                      
          
 
- 
                        算法标签>前缀和          
 
第2题-Tk的素区间
题解思路
1. 前缀和与二次前缀和
- 令
P[i]=∑j=1iaj
(一维前缀和),
Q[i]=∑j=1iP[j]
(前缀和的前缀和)。 - 则任意长度为 x 的子数组和的总和
可化简为

 
2. 筛素数
- 用埃氏筛在 O(nloglogn) 时间内算出长度 1 到 n 哪些是素数。
 
3. 针对素数长度做前缀和
- 构造数组
h[i]=∑2≤y≤iy 为素数g(y)modM, M=998244353. - 每次询问 [l,r] 时,答案就是
(h[r]−h[l−1])modM,
O(1) 返回。 
复杂度分析
- 前缀和 P,Q 和所有 g(x) 计算:O(n)
 - 埃氏筛素数:O(nloglogn)
 - 构造 h:O(n)
 - 每次查询:O(1),共 m 次,加起来 O(m)
 - 总时间复杂度:O(nloglogn+m)
 - 额外空间:O(n)
 
代码实现
Python
import sys
def main():
    input = sys.stdin.readline
    MOD = 998244353
    n, m = map(int, input().split())
    a = list(map(int, input().split()))
    # 1. 前缀和 P 和二次前缀和 Q
    P = [0] * (n+1)
    Q = [0] * (n+1)
    for i in range(1, n+1):
        P[i] = P[i-1] + a[i-1]
        Q[i] = Q[i-1] + P[i]
    # 2. 计算 g[x] mod M
    g = [0] * (n+1)
    for x in range(1, n+1):
        g[x] = (Q[n] - Q[x-1] - Q[n-x]) % MOD
    # 3. 埃氏筛
    is_prime = [True] * (n+1)
    is_prime[0] = is_prime[1] = False
    import math
    for i in range(2, int(math.isqrt(n)) + 1):
        if is_prime[i]:
            for j in range(i*i, n+1, i):
                is_prime[j] = False
    # 4. 构造前缀和 h
    h = [0] * (n+1)
    for i in range(1, n+1):
        h[i] = h[i-1] + (g[i] if is_prime[i] else 0)
        if h[i] >= MOD:
            h[i] -= MOD
    # 5. 回答查询
    for _ in range(m):
        l, r = map(int, input().split())
        ans = (h[r] - h[l-1]) % MOD
        print(ans)
if __name__ == "__main__":
    main()
Java
import java.io.*;
import java.util.*;
public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tk = new StringTokenizer(in.readLine());
        final int MOD = 998244353;
        int n = Integer.parseInt(tk.nextToken());
        int m = Integer.parseInt(tk.nextToken());
        long[] a = new long[n+1];
        tk = new StringTokenizer(in.readLine());
        for (int i = 1; i <= n; i++) {
            a[i] = Long.parseLong(tk.nextToken());
        }
        // 前缀和 P 和 Q
        long[] P = new long[n+1], Q = new long[n+1];
        for (int i = 1; i <= n; i++) {
            P[i] = P[i-1] + a[i];
            Q[i] = Q[i-1] + P[i];
        }
        // g[x]
        long[] g = new long[n+1];
        for (int x = 1; x <= n; x++) {
            g[x] = (Q[n] - Q[x-1] - Q[n-x]) % MOD;
            if (g[x] < 0) g[x] += MOD;
        }
        // 埃氏筛
        boolean[] isPrime = new boolean[n+1];
        Arrays.fill(isPrime, true);
        isPrime[0] = isPrime[1] = false;
        for (int i = 2; i*i <= n; i++) {
            if (isPrime[i]) {
                for (int j = i*i; j <= n; j += i) {
                    isPrime[j] = false;
                }
            }
        }
        // 构造 h
        long[] h = new long[n+1];
        for (int i = 1; i <= n; i++) {
            h[i] = h[i-1] + (isPrime[i] ? g[i] : 0);
            if (h[i] >= MOD) h[i] -= MOD;
        }
        // 回答查询
        PrintWriter out = new PrintWriter(System.out);
        for (int i = 0; i < m; i++) {
            tk = new StringTokenizer(in.readLine());
            int l = Integer.parseInt(tk.nextToken());
            int r = Integer.parseInt(tk.nextToken());
            long ans = (h[r] - h[l-1]) % MOD;
            if (ans < 0) ans += MOD;
            out.println(ans);
        }
        out.flush();
    }
}
C++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 998244353;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector<ll> a(n+1);
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    // 前缀和 P 和 Q
    vector<ll> P(n+1), Q(n+1);
    for(int i = 1; i <= n; i++){
        P[i] = P[i-1] + a[i];
        Q[i] = Q[i-1] + P[i];
    }
    // g[x]
    vector<ll> g(n+1);
    for(int x = 1; x <= n; x++){
        ll v = Q[n] - Q[x-1] - Q[n-x];
        v %= MOD;
        if (v < 0) v += MOD;
        g[x] = v;
    }
    // 埃氏筛
    vector<bool> isPrime(n+1, true);
    isPrime[0] = isPrime[1] = false;
    for(int i = 2; i * i <= n; i++){
        if(isPrime[i]){
            for(int j = i*i; j <= n; j += i){
                isPrime[j] = false;
            }
        }
    }
    // 构造 h
    vector<ll> h(n+1);
    for(int i = 1; i <= n; i++){
        h[i] = h[i-1] + (isPrime[i] ? g[i] : 0);
        if (h[i] >= MOD) h[i] -= MOD;
    }
    // 回答查询
    while(m--){
        int l, r;
        cin >> l >> r;
        ll ans = (h[r] - h[l-1]) % MOD;
        if(ans < 0) ans += MOD;
        cout << ans << "\n";
    }
    return 0;
}
        题目内容
Tk 有一个长度为 n 的数组 a,Tk 定义 g(x)=∑i=1n−x+1∑j=ii+x−1aj,即所有区间长度为 x 的区间和,Tk 会向你询问 m 次,每一次给你个区间 [l,r],你需要求出区间内所有素数 y 对应 g(y) 的和,由于所求值可能比较大,你只需要告诉他答案对 998244353 的取模结果即可。
输入描述
第一行输入两个正整数 n,m(1≤n≤104,1≤m≤2∗105) 表示数组大小和 Tk 的询问次数。
第二行输入 n 个正整数 ai(1≤ai≤109) 表示数组第 i 个位置的值。
接下来 m 行每一行给出两个整数 li,ri(1≤li≤ri≤n) 表示 Tk 第 i 次询问的区间。
输出描述
输出分 m 行,每一行对 Tk 的询问作出回答。
示例1
输入
5 3
1 4 2 7 4
4 4
2 3
1 5
输出
0
64
82
说明
我们可以得到 g(1)=18,g(2)=31,g(3)=33,g(4)=31,g(5)=18
区间 [4,4] 中,没有素数,所以 g(4)=0
区间 [2,3] 中,2 和 3 都是素数,g(2)+g(3)=31+33=64
区间 [1,5] 中,2,3,5 是素数,g(2)+g(3)+g(5)=31+33+18=82