题意:
给出n个数的范围 li 和 ri。从每个范围中各选择出一个数,使得n个数的总和不大于m,且公共gcd值为1。求方案数。
题解:
从1到m,dp出每个数作为gcd值且总和不大于m的情况。
枚举当前的gcd值为d,那么最多有m/d份这样的值。
f[i][j]代表第i个范围使用d的数量不大于j时的情况数,使用滚动数组优化掉一维。
但是最终求出的每个值作为gcd的方案数还要再容斥一下,因为对于某个值x作为gcd的方案数,其实还包含2x,3x...这样的情况。
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10; const int mod = 998244353; int n, m; int l[55], r[55]; int g[N]; int f[N], dp[N]; int get(int x) { return x < 0 ? 0 : dp[x]; } int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) scanf("%d%d", &l[i], &r[i]); for(int d = m; d >= 1; d--) { int t = m / d; for(int i = 0; i <= t; i++) dp[i] = 1; for(int i = 1; i <= n; i++) { int L = (l[i] + d - 1) / d; int R = r[i] / d + 1; for(int j = 1; j <= t; j++) f[j] = ((f[j-1] + get(j-L)) % mod - get(j-R) + mod) % mod; for(int j = 0; j <= t; j++) dp[j] = f[j]; } g[d] = dp[t]; } for(int d = m; d >= 1; d--) { for(int i = 2; i * d <= m; i++) g[d] = (g[d] - g[i*d] + mod) % mod; } printf("%d ", g[1]); }
package main import ( "io" "os" "fmt" "bufio" ) const N int = 1e5+10 const mod int = 998244353 var n, m int var l, r [55]int var g, f, dp [N]int func get(x int) int { if x < 0 { return 0 } else { return dp[x] } } func main() { var input io.Reader = bufio.NewReader(os.Stdin) fmt.Fscan(input, &n) fmt.Fscan(input, &m) for i := 1; i <= n; i++ { fmt.Fscan(input, &l[i]) fmt.Fscan(input, &r[i]) } for d := m; d >= 1; d-- { t := m / d for i := 0; i <= t; i++ { dp[i] = 1 } for i := 1; i <= n; i++ { L := (l[i] + d - 1) / d R := r[i] / d + 1 for j := 1; j <= t; j++ { f[j] = ((f[j-1] + get(j-L)) % mod - get(j-R) + mod) % mod } for j := 0; j <= t; j++ { dp[j] = f[j] } } g[d] = dp[t] } for d := m; d >= 1; d-- { for i := 2; i * d <= m; i++ { g[d] = (g[d] - g[i*d] + mod) % mod } } fmt.Print(g[1]) }