您的位置:首页 > 其它

HDU 6053 TrickGCD(莫比乌斯反演+分块)

2017-07-27 22:15 281 查看

题目链接

TrickGCD

分析

题意其实很简单,反过来其实只要求得 gcd(bi)=1 的个数,然后再用总的组合数减去就行了

而对于求 gcd(bi)=1,bi<=Ai 由mobius公式

∑x=1min{Ai}μ(x)∏i=1nfloor(Ai/x)

关键就在于如何快速计算它的值,这里我采用的是 nlogn 的算法.用 sum[i] 表示 Aj<=i的数的总数 对于每一个 x 连续倍数的一段(比如i−1,i+d−1 这一段除d都是i/d)的值是相同,我们就可以直接求个幂次(sum[i+d−1]−sum[i−1])这样总的复杂度就是 O(nlogn) 在乘以快速幂的复杂度

//Problem : 6053 ( TrickGCD )     Judge Status : Accepted
//RunId : 21361279    Language : G++    Author : zouzhitao
//Code Render Status : Rendered By HDOJ G++ Code Render Version //0.01 Beta
#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define PI acos(-1)
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define INF64 0x3f3f3f3f3f3f3f3f
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define ms(x,v) memset((x),(v),sizeof(x))
using namespace std;
const int MOD = 1e9+7;
const double eps = 1e-8;
typedef long long LL;
typedef long double DB;
typedef pair<int,int> Pair;
const int maxn = 1e5+10;

int mu[maxn],prime[maxn],cnt;
int sum[maxn];
void mobius(){
cnt =0;
mu[1] = 1;
memset(prime,0,sizeof(prime));
for(int i = 2 ; i<maxn ; ++i){
if(!prime[i]){
prime[cnt++] = i;
mu[i] =-1;
}
for(int j=0 ; j<cnt && i*prime[j]<maxn ; ++j){
prime[i*prime[j]] = 1;
if(i%prime[j])mu[prime[j]*i] = -mu[i];
else {
mu[i*prime[j]] = 0;
break;
}
}
}
// sum_mu[0] = 0;
// for(int i=1 ; i<maxn ; ++i)
//     sum_mu[i] = sum_mu[i-1]+mu[i];
}
LL power_mod(LL x,LL n){
LL ret =1;
while (n) {
if(n&1)ret = ret*x %MOD;
x= x*x %MOD;
n >>=1;
}
return ret;
}
int num[maxn];
inline LL F(){
int sz =0;
for(int i=1;  i<maxn ; ++i)
if(num[i]){sz= i;break;}
int maxv = 0;
for(int i=maxn-1 ; i>=0 ; --i)
if(num[i]){
maxv = i;break;
}
maxv++;
LL ans = 0;
for(int d = 1 ; d <=sz; ++d){
LL val =mu[d];
if(val)
for(int i=d; i<maxv ; i+=d){
val *= power_mod(i/d,sum[min(i+d,maxv)-1]- sum[i-1]);
val %= MOD;
}
ans += val;
ans %=MOD;
}
return ans;
}

int main() {

// std::ios::sync_with_stdio(false);
// std::cin.tie(0);
int n;
mobius();
int T;
scanf("%d",&T );
int kase = 0;
while (T--) {
int n;
scanf("%d",&n );

ms(num,0);
sum[0] = 0;
for(int i=1;  i<=n ; ++i){
int x; scanf("%d", &x);;num[x]++;
}
for(int i=1 ; i<maxn ; ++i){
sum[i] = sum[i-1] + num[i];
}
LL ans = F();
LL val = 1;
for(int i=1 ; i<maxn ; ++i){
val *= power_mod(i,num[i]);
val %= MOD;
}
printf("Case #%d: %lld\n",++kase,(val-ans+MOD) %MOD );
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息