题目链接:点击打开链接
题解思路:应用莫比乌斯就是处理容斥,这里用原始的容斥肯定超时。
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int n,m;
const int mx = 1e5+10,mod = 1e9+7;
int mu[mx],pri[mx],num[mx],sum[mx<<1];
bool vis[mx];
void init(){
int top = 0;
for(int i=2;i<mx;i++){
if(!vis[i]){
pri[top++] = i;
mu[i] = -1;
}
for(int j=0;j<top&&pri[j]*i<mx;j++){
int val = i*pri[j];
vis[val] = 1;
if(i%pri[j]==0){
mu[val] = 0;
break;
}
mu[val] = -mu[i];
}
}
}
ll kuaisu(ll x,ll y){
ll ans = 1;
while(y){
if(1&y) ans = ans*x%mod;
y >>= 1;
x = x*x%mod;
}
return ans;
}
int main(){
int t,cas = 1;
scanf("%d",&t);
init();
while(t--){
memset(sum,0,sizeof(sum));
scanf("%d",&n);
int minn = 1e9,maxx = 0;
for(int i=1;i<=n;i++){
scanf("%d",num+i);
minn = min(minn,num[i]);
maxx = max(maxx,num[i]);
sum[num[i]]++;
}
for(int i=mx-2;i>=0;i--) sum[i] += sum[i+1]; //大等i的有几个
ll ans =0;
for(int i=2;i<=minn;i++){
ll cnt = 1,ret = -mu[i];
int p = i;
while(p<=maxx)
ret = (ret*kuaisu(cnt,sum[p]-sum[p+i]))%mod, p += i,cnt++;
ans = (ans+ret+mod)%mod;
}
printf("Case #%d: %I64d\n",cas++,ans);
}
return 0;
}
也可以用递归形式的容斥可行
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int n,m,minn,maxx;
const int mx = 1e5+10,mod = 1e9+7;
int pri[mx],num[mx],sum[mx<<1];
bool vis[mx];
void init(){
int top = 0;
for(int i=2;i<mx;i++){
if(!vis[i]) pri[top++] = i;
for(int j=0;j<top&&pri[j]*i<mx;j++){
vis[i*pri[j]] = 1;
if(i%pri[j]==0) break;
}
}
}
ll kuaisu(ll x,ll y){
ll ans = 1;
while(y){
if(1&y) ans = ans*x%mod;
y >>= 1;
x = x*x%mod;
}
return ans;
}
ll dfs(int cnt,int pos,ll val){
ll ans = 0;
for(int i = pos+1;val*pri[i]<=minn;i++){
ll s = val*pri[i],ret = 1;
int p = s,c = 1;
while(p<=maxx) ret = (ret*kuaisu(c++,sum[p]-sum[p+s]))%mod, p += s;
if(cnt&1) ans = (ans+ret)%mod;
else ans = (ans-ret+mod)%mod;
ans = (ans+dfs(cnt+1,i,s))%mod;
}
return ans;
}
int main(){
int t,cas = 1;
scanf("%d",&t);
init();
while(t--){
memset(sum,0,sizeof(sum));
scanf("%d",&n);
minn = 1e9,maxx = 0;
for(int i=1;i<=n;i++){
scanf("%d",num+i);
minn = min(minn,num[i]);
maxx = max(maxx,num[i]);
sum[num[i]]++;
}
for(int i=mx-2;i>=0;i--) sum[i] += sum[i+1]; //大等i的有几个
printf("Case #%d: %lld\n",cas++,dfs(1,-1,1));
}
return 0;
}