背景:最近想玩玩数论,偶然去51nod找了一题和质数相关的题目,最讨厌做质数相关的题了QAQ。题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1439
题意:
给定n个数,q个操作
下面n个数 a1-an
开始有一个空的集合
每个操作一个数字 u (1<=u<=n) 表示把 a[u] 插入集合(若集合中没有a[u]), 否则就把a[u]从集合中删除
每个操作结束后输出 当前集合内有多少对数 是互质的。
这道题关键解决一个问题就是:已知有一些数的集合S,给出一个下标index,问arr[index]能与S集合中的数字组成多少对质数对(PS:C集合可能存在多个数值相同的数,但由于下标不同,故算作不同的数)
做法:分解质因子,dfs爆搜做容斥。
变量解析:
c[]:存因子,除去1。
f[]:存因子出现的次数
vector<int> G[]:G[i]为值为i的质因子的结合。
set<int> s;
#include <iostream>
#include <fstream>
#include <string>
#include <time.h>
#include <vector>
#include <map>
#include <queue>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 5e5+10;
int prime[maxn],primenum;
int n,q;
int arr[maxn];
vector<int> G[maxn];
ll ans;
set<int> s;
int top,one;
int f[maxn];
int c[maxn];
template<class T>
inline bool rd(T &ret){
char c;
int sgn;
if(c = getchar(),c == EOF) return 0;
while(c != '-' &&(c < '0' ||c > '9')) c = getchar();
sgn = (c == '-')?-1:1;
ret = (c == '-')?0:(c-'0');
while(c = getchar(),c>='0'&&c<='9') ret = ret * 10 + (c-'0');
ret *= sgn;
return 1;
}
template<class T>
inline void pt(T x){
if(x<0){
putchar('-');
x = -x;
}
if(x>9) pt(x/10);
putchar(x%10+'0');
}
void PRIME(ll max_prime){
primenum = 0;
prime[primenum++] = 2;
for(ll i = 3; i <= max_prime; i += 2){
for(ll j = 0; j < primenum; j++){
if(i%prime[j] == 0) break;
else if(prime[j]>sqrt((double)i)||j == primenum - 1){
prime[primenum++] = i;
break;
}
}
}
}
void pre(int index,int val){
for(int i = 0; prime[i] * prime[i] <= val;i++){
if(val%prime[i] == 0){
while(val%prime[i] ==0)
val /= prime[i];
G[index].push_back(prime[i]);
}
}
if(val != 1) G[index].push_back(val);
}
void dfs(int t,int index,int cnt,int flag,int val){//flag标记1插入,-1删除
printf("val: %d\n",val);
if(t >= (int)G[index].size()){
if(cnt == 0) return;
ans += f[val] * flag;
c[top++] = val;
return;
}
dfs(t+1, index , cnt , flag , val);
dfs(t+1, index , cnt+1 , flag*(-1), val*G[index][t]);
}
int main(){
PRIME(maxn);
rd(n);rd(q);
for(int i = 1; i <= n; i++){
rd(arr[i]);
pre(i,arr[i]);//预处理出下标为i,值为arr[i]的质因子
}
for(int i = 1;i <= n; i++){
for(int j = 0; j < G[i].size(); j++){
printf("%d ",G[i][j]);
}
printf("\n");
}
ans = 0,one = 0;
int index;
while(q--){
rd(index);
if(arr[index] == 1){//index下标对应的值是1
if(s.count(index)){
s.erase(index);
ans -= s.size();
}
else{
ans += s.size();
s.insert(index);
}
}else{
if(s.count(index)){//存了index这个下标
s.erase(index);
ans -= s.size();//先当成都是互质的
top = 0;
dfs(0,index,0,-1,1);
ans--;
for(int i = 0; i < top; i++){
f[c[i]]--;
}
}else{
ans += s.size();
s.insert(index);
top = 0;
dfs(0,index,0,1,1);
for(int i = 0; i < top; i++){
f[c[i]]++;//f[i]表示质数为i的个数
}
}
}
pt(ans);
puts("");
}
return 0;
}
例如集合中已经存在了1、2、3、4、6,那么f[2] = 3,f[3] = 2,f[6] = 1,此时集合中质数对有6对。当往S中插入30,假设集合中所有数与30都组成质数对,6+5=11;接下来bfs容斥,结果为11--f[2]-f[3]-f[5]+f[6]+f[10]+f[15]-f[30] = 11 - 3 -2 -0 +1 +0 +0 +0 = 7;然后f[2],f[3],f[5],f[6],f[10,f[15],f[30]都加1。