题目:
http://codeforces.com/problemset/problem/547/C
题意:
n种啤酒,每种啤酒有相应的毫升数,q个询问。一开始的酒架是空的,输入啤酒标号x,若x已在酒架上,则将其取下;若不在则添加。求出每次询问的时候 i<j 且gcd(ai,aj)=1的对数。
思路:
使用普通的方法会超时!T^T。
应该知道的是:50万以内的数的质因子不超过 6个,所以可以使用容斥定理。
将50万以内的数的质因子预处理出来。使用容斥定理快速得到gcd=1的数量。
AC.
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn = 5e5+5;
vector<int> q[maxn];
int cnt[maxn], a[maxn], vis[maxn];
int init()
{
for(int i = 2; i < maxn; ++i) {
if(q[i].empty()) {
for(int j = i; j < maxn; j+=i) {
q[j].push_back(i);
}
}
}
} //求1~maxn 的质因子
int query(int x)
{
int sum = 0;
vector<int> now = q[x];
int len = now.size();
for(int i = 0; i < (1<<len); ++i) {
int s = 1, sign = 1;
for(int j = 0; j < len; ++j) {
if(i & (1<<j)) {
s *= now[j];
sign *= -1;
}
}
sum += cnt[s]*sign; //s = 1是 sign为总为1,所以可以得到互质的数的个数
}
return sum;
}
int add(int x, int f)
{
vector <int> now = q[x];
int len = now.size();
for(int i = 0; i < (1<<len); ++i) {
int s = 1;
for(int j = 0; j < len; ++j) {
if(i & (1<<j)) {
s *= now[j];
}
}
cnt[s] += f;
}
}
int main()
{
// freopen("in", "r", stdin);
init();
int n, q;
while(~scanf("%d %d", &n, &q)) {
memset(cnt, 0, sizeof(cnt));
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
ll ans = 0;
while(q--) {
int x;
scanf("%d", &x);
if(!vis[x]) {
ans += query(a[x]);
add(a[x], 1);
vis[x] = 1;
}
else {
add(a[x], -1);
ans -= query(a[x]);
vis[x] = 0;
}
printf("%lld\n", ans);
}
}
return 0;
}