CF1559E Mocha and Stars
思路
观察数据范围,大概
Θ
(
n
m
log
?
m
)
\Theta(nm\log m)
Θ(nmlogm) 是可行的
那么有了一个基本的想法,
d
p
i
,
j
dp_{i,j}
dpi,j? 表示选到了第 i 位,且当前所选数的和是 j 的方法数
但是这样最后的 dp 值里面会有 gcd 不为 1 的情况
以下默认省略和不大于m
考虑容斥原理
如果 gcd 不为 1 ,那么它一定是某一个质数的倍数
所以减去所有 gcd 为质数倍数的情况,但是这样子我们会将一些 gcd 为特定值的部分多减去一次, 比如 gcd 为 6 的情况会被多减一次, 因为 2 减 3 也减
所以需要加上一些情况,可以发现所有因式分解下有两个及以上不同的质数的 gcd 都会被多减,那么加上 gcd 为所有因式分解中仅为两个不同素数的数(6, 15)的倍数的情况
会发现,这样又会有所有因式分解中有三个及以上不同的质数的 gcd 被多加,所以减去 gcd 为所有因式分解中仅为三个不同素数的数(30, 42)的倍数的情况
…递归下去
实际上,这种容斥系数有个专门的函数来表示,叫做莫比乌斯函数(我也是才知道)
暴力分解数的话,复杂度为
Θ
(
m
m
)
\Theta(m\sqrt{m})
Θ(mm
?)? (wf算了1e12,但实际加一点剪枝远跑不满,本地循环内计数跑了1e8次)
但因为它是积性函数,也可以线性筛内递推求出来
那么知道容斥方法了,考虑怎么计算
如果要计算 gcd 为 x 的倍数的方案数,那么我们首先将 m/x , l/x(上取整), r/x(下取整)
这样当取一个数 i 的时候,相当于取了 i*x ,这就保证了答案是 gcd 为 x 的倍数的方案数
dp 时我做了一个前缀和优化,也可以另开一个数组用背包
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;
const int mod = 998244353;
const int inf_int = 0x7fffffff;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;
int n, m;
ll l[51], r[51];
ll dp[51][100100];
ll sol(cint x) {
for(int i=0; i<=m/x; i++) dp[0][i] = 1;
for(int i=1; i<=n; i++) {
for(int j=1; j<=m/x; j++) {
dp[i][j] = 0;
int dl = ceil((double)l[i]/x), dr = r[i]/x;
if(dl > dr) return 0;
if(j-dl >= 0) dp[i][j] += dp[i-1][j-dl];
if(j-dr-1 >= 0) dp[i][j] -= dp[i-1][j-dr-1];
dp[i][j] += dp[i][j-1];
dp[i][j] = (dp[i][j] + mod) % mod;
}
}
return dp[n][m/x];
}
int check(int x) {
if(x == 1) return 1;
int num = 0;
int r = sqrt(x);
for(int i=2; i<=r; i++) {
if(!(x%(i*i))) return 2;
if(!(x%i)) {
++ num;
x /= i;
}
}
if(x > 1) ++num;
return !(num&1);
}
int main() {
cin >> n >> m;
for(int i=1; i<=n; i++) cin >> l[i] >> r[i];
ll ans = 0;
for(int i=1; i<=m; i++) {
if(check(i) == 1) ans += sol(i);
else if(check(i) == 0) ans -= sol(i);
ans = (ans+mod) % mod;
}
cout << ans << endl;
return 0;
}
|