题目链接
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); } }
|
你最愿意做的哪件事,才是你的天赋所在