题目链接
题目大意
有 n n n 种啤酒,编号从 1 1 1 到 n n n 。第 i i i 瓶啤酒上面有 a i a_{i} ai 毫升的泡沫。
回答
q
q
q 个查询。最初架子是空的。在每个操作中,给一个编号
X
X
X。如果编号为
X
X
X的啤酒已经在架子上,应该从架子上取下它,否则他应该把它放在架子上。
每次询问后,输出该架子的分数。他们认为货架的分数是满足
i
<
j
i<j
i<j 并且
g
c
d
(
a
i
,
a
j
)
=
1
gcd(a_{i},a_{j})=1
gcd(ai,aj)=1的数对
(
i
,
j
)
(i,j)
(i,j)的个数。
n n n 和 q q q( 1 ≤ n 1\le n 1≤n, q ≤ 2 × 10 5 q\le 2\times {10}^{5} q≤2×105),不同种类的啤酒数量和查询次数。
下一行包含 n n n个由空格分隔的整数, a 1 , a 2 , … , a n a_{1},a_{2},\dots,a_{n} a1,a2,…,an( 1 ≤ a i ≤ 5 × 1 0 5 1\le a_{i}\le 5\times10^{5} 1≤ai≤5×105 ),表示各种啤酒顶部的泡沫量。
接下来 q q q 行一行包含一个查询。每个查询一个整数 X X X( 1 ≤ X ≤ N 1\le X\le N 1≤X≤N),表示应从货架上添加或移除的啤酒的编号。
思路
考虑添加或移除走某个数之后 a n s ans ans 的变化量。
比如当前有 k k k 个数,加入一个数 x x x 只考对 a n s ans ans 增量,考虑容斥 △ = \bigtriangleup = △= k − c n t ( g c d k-cnt(gcd k−cnt(gcd ≠ \neq = 1 ) 1) 1) ,当 k k k 中某个数与 x x x 具有公共因子时 g c d ≠ 1 gcd \neq 1 gcd=1。
任意一个数 n n n 都可以表示为 n = p 1 α 1 ⋅ p 2 α 2 ⋅ ⋅ ⋅ p k α k n=p_1^{\alpha 1} \cdot p_2^{\alpha 2} \cdot \cdot \cdot p_k^{\alpha k} n=p1α1⋅p2α2⋅⋅⋅pkαk ,只需要关注质因数的种类,又因为 1 ≤ a i ≤ 5 × 1 0 5 1\le a_{i}\le 5\times10^{5} 1≤ai≤5×105 , 2 ∗ 3 ∗ 5 ∗ 7 ∗ 11 ∗ 13 ∗ 17 = 510510 > 1 0 5 2*3*5*7*11*13*17=510510 > 10^{5} 2∗3∗5∗7∗11∗13∗17=510510>105,故 p p p 的种类最多有六个,那也就是最多有 2 6 2^6 26 种组合,时间复杂度 O ( q ∗ 2 6 ) O(q*2^{6}) O(q∗26)
code
#include <bits/stdc++.h>
#define int long long
#define ll long long
#define pii pair<int, int>
using namespace std;
const int N = 5e5 + 1000;
int prime[N], sum[N], cnt = 0;
bool st[N], vis[N];
vector<int> p[N];
int ans = 0;
void is_prime(int n)
{
for (int i = 2; i <= n; ++i)
{
if (!st[i])
prime[cnt++] = i;
for (int j = 0; j < cnt && i * prime[j] <= n; ++j)
{
st[i * prime[j]] = 1;
if (i % prime[j] == 0)
break;
}
}
}
void query(int x, int op)
{
for (int i = 0; i < (1ll << p[x].size()); ++i)
{
int now = 1, num = 0;
for (int j = 0; j < p[x].size(); ++j)
if ((1ll << j) & i)
now *= p[x][j], num++;
if (num & 1)
ans -= op * sum[now];
else
ans += op * sum[now];
}
}
void updata(int x, int op)
{
for (int i = 0; i < (1ll << p[x].size()); ++i)
{
int now = 1;
for (int j = 0; j < p[x].size(); ++j)
if ((1ll << j) & i)
now *= p[x][j];
sum[now] += op;
}
}
void solve()
{
int n, q;
cin >> n >> q;
for (int i = 1; i <= n; ++i)
{
int x;
cin >> x;
for (int j = 0; j < cnt && prime[j] <= sqrt(x); ++j)
{
if (x % prime[j] == 0)
{
p[i].push_back(prime[j]);
while (x % prime[j] == 0)
x /= prime[j];
}
}
if (x > 1)
p[i].push_back(x);
}
while (q--)
{
int x;
cin >> x;
if (!vis[x])
query(x, 1), updata(x, 1);
else
updata(x, -1), query(x, -1);
cout << ans << '\n';
vis[x] ^= 1;
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
is_prime(5e5 + 10);
int t = 1;
while (t--)
solve();
return 0;
}