题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5072
题意:在1e5个数(<=1e5)中,找出有多少对(a,b,c) , 满足 ( gcd(a,b)==1 && gcd(a,c)==1 && gcd(b,c)==1 ) || ( gcd(a,b)!=1 && gcd(a,c)!=1 && gcd(b,c)!=1 ) .
这很像白书上一道双色三角形计数模型,即有n个点,一直每个点可连出x条红边和n-1-x条绿边,问有多少个双色多边形? ans = n*(n-1)*(n-2)/6 - Sigma( a[i]*(n-1-a[i]) ) / 2 ;
那么这道题可以将互素看做红边,非互素看做绿边。现在我们只需找到每个数有多少和他互素即可。使用容斥定理。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
const int maxn = 100000;
vector<int> pri[N];
ll cnt[N];
void init(){
for(int i=0;i<=maxn;i++)
pri[i].clear();
for(int i=2;i<=maxn;i++) if(pri[i].size()==0)
for(int j=i;j<=maxn;j+=i)
pri[j].push_back(i);
}
void add(int x,int y){
int size = pri[x].size();
for(int i=0;i<(1<<size);i++){
int z = 1;
for(int j=0;j<size;j++)
if(i&(1<<j))
z*=pri[x][j];
cnt[z] += y;
}
}
ll cal(int x){
ll ans = 0;
int size = pri[x].size();
for(int i=0;i<(1<<size);i++){
int z = 1;
int fl = 1;
for(int j=0;j<size;j++)
if(i&(1<<j)){
z*=pri[x][j];
fl *= -1;
}
ans += cnt[z]*fl;
}
return ans;
}
int a[N];
ll b[N],c[N];
int main(){
init();
int T,n; cin >> T;
while(T--){
memset(cnt,0,sizeof(cnt));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
add(a[i],1);
}
for(int i=1;i<=n;i++){
add(a[i],-1);
ll xx = cal(a[i]);
b[i] = xx;
c[i] = n-1 - xx;
add(a[i],1);
}
ll ans = (ll)n*(ll)(n-1)*(ll)(n-2)/6;
ll count = 0;
for(int i=1;i<=n;i++){
count += (ll)b[i]*(ll)c[i];
}
ans -= count/2 ;
printf("%lld\n",ans);
}
return 0;
}
本文解析了HDU 5072题目的解题思路,通过建立双色三角形计数模型,利用容斥原理计算每个数与其他数互素的数量,最终得出所有满足条件的数对个数。
1万+

被折叠的 条评论
为什么被折叠?



