你最愿意做的哪件事,才是你的天赋所在

0%

HDU-6053-莫比乌斯反演

题目链接

HDU-6053

思路

这道题训练的时候没想到处理出F的方法,同样是用了分块的方法,学到了。
首先我们根据题目,

然后我们根据套路的莫比乌斯反演得出

其中我们要算的答案就是$f(2)+f(3)+f(4)+\cdots+f(min(a[]))$
实际上的就是$F(1)-f(1)$
也就是

所以我们先处理出$F[]$数组的数据,然后再用O(n)的时间算出答案即可。
说一下怎么$n^\frac{1}{2}$次方求出F吧,我们先求出前缀和pre[j]-pre[i]表示i-j中的数字出现多少次,然后我们就可以用分块的思想,对每个F,原式子是这样的

然后我们发现可以枚举$\lfloor\frac{a[0]}{x}\rfloor$的结果,只有$n^\frac{1}{2}$个,然后记录从$(j+1)i-ji-1的范围中a[]出现了几次$再用快速幂即可,时间复杂度多了一个快速幂的logn,这样就可以写了

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e5+10;
bool vis[maxn];
int prime[maxn],cnt;
int mu[maxn];
int pre[maxn*2];
ll F[maxn];
const int md= 1e9+7;
void init()
{
mu[1]=1;
for(int i=2;i<maxn;i++)
{
if(!vis[i])
{
prime[cnt++]=i;
mu[i]=-1;
}
for(int j=0;j<cnt&&i*prime[j]<=maxn;j++)
{
vis[i*prime[j]]=true;
if(i%prime[j])
{
mu[i*prime[j]]=-mu[i];
}
else
{
mu[i*prime[j]]=0;
break;
}
}
}
}
ll quick_pow(ll a,ll b)
{
ll ans = 1;
while(b)
{
if(b&1)ans=(ans*a)%md;
b>>=1;
a=(a*a)%md;
}
return ans;
}
int main()
{
int t;
scanf("%d",&t);
init();
for(int cas=1;cas<=t;cas++)
{
int n;
scanf("%d",&n);
memset(pre,0,sizeof(pre));
ll tans = 1,ans=0;
int maxx=0,minn=1000000;
for(int i=0;i<n;i++)
{
int x;
scanf("%d",&x);
pre[x]++;
maxx = max(maxx,x);
minn = min(minn,x);
}
for(int i=1;i<maxn;i++)pre[i]+=pre[i-1];
for(int i=1;i<=minn;i++)F[i]=1;
for(int i=2;i<=minn;i++)
{
for(int j=1;j*i<=1e5;j++)
{
ll up = min((j+1)*i-1,100000);
F[i]=(F[i]*quick_pow(j,pre[up]-pre[i*j-1]))%md;;
}
}
for(int i=2;i<=minn;i++)
{
ans=(ans-mu[i]*F[i])%md;
if(ans<0)ans+=md;
}
printf("Case #%d: %lld\n",cas,ans);
}
}
你最愿意做的哪件事,才是你的天赋所在
-------------你最愿意做的哪件事才是你的天赋所在-------------