[题解]CF1884D Counting Rhyme
思路
首先可以转化一下题意,发现对于一个好的数对 \((i,j)\) 成立,一定满足无法在 \(a\) 中找到一个 \(a_k\) 为 \(\gcd(i,j)\) 的因子。
不妨设 \(dp_i\) 表示满足 \(\gcd(a_p,a_q) = i\) 的数对数量,\(num_i\) 表示 \(i\) 在 \(a\) 中出现的次数。
那么令 \(t = \sum_{k = 2}^{k \times i \leq n}{num_{k \times i}}\)。
显然有:
\[
dp_i = \frac{t \times (t - 1)}{2} - \sum_{k = 2}^{k \times i \leq n}{dp_{k \times i}}
\]
那么同时记录 \(vis_i\) 表示在 \(a\) 中是否存在 \(i\) 的因子,如果没有出现过,则将答案加上 \(dp_i\)。
这些东西都是可以在一遍埃筛中解决的,时间复杂度为 \(\Theta(n \log n)\)。
Code
#include <bits/stdc++.h>
#define re register
#define int long long
using namespace std;
const int N = 1e6 + 10;
int T,n;
int arr[N],dp[N],num[N];
bool vis[N];
inline int read(){
int r = 0,w = 1;
char c = getchar();
while (c < '0' || c > '9'){
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9'){
r = (r << 3) + (r << 1) + (c ^ 48);
c = getchar();
}
return r * w;
}
inline void solve(){
int ans = 0;
n = read();
for (re int i = 1;i <= n;i++){
dp[i] = num[i] = 0;
vis[i] = false;
}
for (re int i = 1;i <= n;i++){
arr[i] = read();
num[arr[i]]++;
vis[arr[i]] = true;
}
for (re int i = n;i;i--){
int t = 0;
for (re int j = i;j <= n;j += i){
t += num[j];
dp[i] -= dp[j];
vis[j] |= vis[i];
}
dp[i] += t * (t - 1) / 2;
}
for (re int i = 1;i <= n;i++){
if (!vis[i]) ans += dp[i];
}
printf("%lld\n",ans);
}
signed main(){
T = read();
while (T--) solve();
return 0;
}

浙公网安备 33010602011771号