*题解:P13568 [CCPC 2024 重庆站] 乘积,欧拉函数,求和
解析
首先,我们有公式:
其中 \(p\) 为质数。
于是就有两种思路,定 \(x\) 求 \(\prod_{p \mid x}(1 - \frac{1}{p})\) 之和与定质因子求 \(x\) 之和。前者发现行不通,考虑后者。考虑状压,设 \(f_{i,S}\) 表示考虑了前 \(i\) 个数,已有的质因子集合为 \(S\) 时的 \(x\) 之和。但是由于值域 $ V = 3000$ 以内的质数很多所以这个做法仍然倒闭。但是我们发现,对于任意一个 \(a_i\),大于 \(\sqrt{a_i}\) 的质因子至多只有一个,而 \(\le \sqrt{V}\) 的质数只有 \(16\) 个,故尝试把大于 \(\sqrt{V}\) 的质因子单独摘出来。
具体地,记 \(S_x\) 为 \(x\) 的质因子集合,则普通的转移方程是这样的:
现在要考虑大于 \(\sqrt{V}\) 的质因子 \(p_i\),那么就先将所有 \(a_i\) 按 \(p_i\) 排序。既然 \(p_i\) 没法存到状态里,那就把它的贡献乘到值里去。故更改状态定义为 \(f_{i,0/1,S}\) 表示考虑了前 \(i\) 个数,在前面没有 / 有选以 \(p_i\) 为质因子的数,选取了的 \(\le \sqrt{V}\) 的质因子集合为 \(S\) 时,\(x\) 与选取了的 \(>\sqrt{V}\) 的质因子的所有组合的乘积之和。那么有:
此外,当 \(p_i = p_{i - 1}\) 时,有:
否则有:
直接转移时间复杂度 \(O(2^{2t}n)\),其中 \(t\) 为 \(\le \sqrt V\) 的质数个数,无法通过。但我们发现 \(\lvert S_{a_i}\rvert\) 很小,因为 \(2 \times 3\times 5 \times 7 \times 11\times 13 = 30030 > V\),故 \(\le V\) 的数不同质因子个数至多有 \(5\) 个,借此可以缩小 \(T\) 的可选范围。但还是无法通过,究其根本是因为我们在尝试求有哪些集合 \(T\) 对 \(S_{a_i}\) 取并后会变成 \(S\),而取并没有逆运算,导致我们只能枚举。正难则反,不妨考虑刷表法,即看当前状态可以转移到哪些状态,这样做的转移方程为:
此外,若 \(p_i \not= p_{i + 1}\),则有:
否则有:
时间复杂度 \(O(2^tn)\)。
想到了的地方 / 思考过的做法
线性筛求 \(\varphi\)。
没想到的地方
利用公式拆贡献。
为什么没想到 / 如何想到
认为 \(x\) 太大,无法利用公式根号复杂度求 \(\varphi\),没有把式子实际列出来观察。
代码
前面的注释是自己做题时的思路,在此题中跟正解无关。
/*
f[i] 表示前 i 个数 - 1 组成的集合的子集元素乘积之和
f[i] = f[i - 1] + f[i - 1] * (a[i] - 1)
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 2000,M = 16 + 2,V = 3000,mod = 998244353;
int f[2][2][(1 << M) + 5];
int mul[(1 << M) + 5];
int a[N],s[N],p[N],val[V + 5];
struct S{
int p,s,a;
friend bool operator < (S a,S b){
return a.p < b.p;
}
};
int qmi(int a,int b){
int res = 1;
while(b){
if(b & 1) res = 1ll * res * a % mod;
b >>= 1;
a = 1ll * a * a % mod;
}
return res;
}
int inv(int x){
return qmi(x,mod - 2);
}
int get(int p){
return val[p];
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
// freopen("math.in","r",stdin);
// freopen("math.out","w",stdout);
vector<int> pri;
for(int i=2;i * i <= V;i++){
bool flag = true;
for(int j=2;j * j <= i;j++){
if(i % j == 0){
flag = false;
break;
}
}
if(flag){
pri.push_back(i);
}
}
val[1] = 1;
for(int i=2;i<=V;i++){
val[i] = 1ll * (i - 1) * inv(i) % mod;
}
int n;
cin>>n;
vector<S> vec;
vec.push_back({-1,-1,-1});
for(int i=1;i<=n;i++){
cin>>a[i];
int aa = a[i];
for(int j=0;j<pri.size() && pri[j] <= a[i];j++){
while(a[i] % pri[j] == 0){
s[i] |= (1 << j);
a[i] /= pri[j];
}
}
if(a[i] * a[i] > 3000){
p[i] = a[i];
}else{
p[i] = 1;
int pos = lower_bound(pri.begin(),pri.end(),a[i]) - pri.begin();
if(a[i] != 1)
s[i] |= (1 << pos);
}
vec.push_back({p[i],s[i],aa});
}
for(int i=0;i<(1 << 16);i++){
mul[i] = 1;
for(int j=0;j<16;j++){
if(i & (1 << j)){
mul[i] = 1ll * mul[i] * get(pri[j]) % mod;
}
}
}
sort(vec.begin(),vec.end());
f[0][0][0] = 1;
for(int i=0;i<n;i++){
int b = i & 1;
int p = vec[i + 1].p,s = vec[i + 1].s,a = vec[i + 1].a,lp = vec[i].p;
for(int j=0;j<(1 << 16);j++){
bool x = p != 1;
f[b ^ 1][0][j] = (f[b ^ 1][0][j] + f[b][0][j]) % mod;
f[b ^ 1][x][j | s] = (f[b ^ 1][x][j | s] + 1ll * f[b][0][j] * get(p) % mod * a % mod) % mod;
if(p != lp){
f[b ^ 1][0][j] = (f[b ^ 1][0][j] + f[b][1][j]) % mod;
f[b ^ 1][x][j | s] = (f[b ^ 1][x][j | s] + 1ll * f[b][1][j] * get(p) % mod * a % mod) % mod;
}
if(p == lp){
f[b ^ 1][1][j] = (f[b ^ 1][1][j] + f[b][1][j]) % mod;
f[b ^ 1][x][j | s] = (f[b ^ 1][x][j | s] + 1ll * f[b][1][j] * a % mod) % mod;
}
}
for(int j=0;j<(1 << 16);j++){
f[b][0][j] = f[b][1][j] = 0;
}
}
int res = 0;
for(int i=0;i<(1 << 16);i++){
int b = n & 1;
res = (res + 1ll * mul[i] * f[n & 1][0][i] % mod + 1ll * mul[i] * f[n & 1][1][i] % mod) % mod;
}
cout<<res;
return 0;
}

浙公网安备 33010602011771号