比赛----题解|2019年CCPC - 网络赛E(huntian oy【杜教筛】)

题目:
HDU---6706:huntian oy
题意:
给定N,a,b,求下面式子的值(求和后再mod 1e9+7):
比赛----题解|2019年CCPC - 网络赛E(huntian oy【杜教筛】)
文章图片

分析:
一直怯于杜教筛不敢去学习【一看就会的杜教筛】,今天终于迈出了这一步,才发现并没有那么难(哈哈哈)
首先有:gcd(i^a-j^a,i^b-j^b) = i^(gcd(a,b))-j^(gcd(a,b)) = i - j(a,b互质),熟悉欧拉函数就容易转化为:
比赛----题解|2019年CCPC - 网络赛E(huntian oy【杜教筛】)
文章图片

比赛----题解|2019年CCPC - 网络赛E(huntian oy【杜教筛】)
文章图片

比赛----题解|2019年CCPC - 网络赛E(huntian oy【杜教筛】)
文章图片

那么就要快速求得i*phi[i]的前缀和即可,显然 f(i) = i*phi[i] 这是个积性函数,那就可以套用杜教筛来求,设积性函数h,g,满足:h = f*g,将其表示成狄利克雷卷积形式:
【比赛----题解|2019年CCPC - 网络赛E(huntian oy【杜教筛】)】比赛----题解|2019年CCPC - 网络赛E(huntian oy【杜教筛】)
文章图片

那么令g函数等于单位函数id,即可求得h(n) = (f*g)(n) = n^2,令S(n)表示f函数的前缀和,则有:
比赛----题解|2019年CCPC - 网络赛E(huntian oy【杜教筛】)
文章图片

前面求和用平方求和公式O(1)求的,后面整除分块+递归求的即可
代码:

#include using namespace std; typedef long long LL; const int mod = 1e9+7; const int maxn = 5e6+205; const int inv2 = 500000004; const int inv6 = 166666668; map mp; int prime[maxn],phi[maxn],cnt; void init(){ phi[1] = 1; for(int i = 2; i < maxn; ++i){ if(!phi[i]) phi[i]=i-1,prime[cnt++]=i; for(int j = 0; j= mod) phi[i] -= mod; } } int solve(int n){ if(n < maxn) return phi[n]; if(mp.count(n)) return mp[n]; int res = 0; for(int d=2,last; d<=n; d=last+1){ int tep = n/d; last = n/tep; res += 1ll*(last+d)*(last-d+1)%mod*inv2%mod*solve(tep)%mod; if(res >= mod) res -= mod; } return mp[n] = (1ll*n*(n+1)%mod*(2*n+1)%mod*inv6%mod-res+mod)%mod; } int main(){ int T,n,a,b; init(); scanf("%d",&T); while(T--){ scanf("%d %d %d",&n,&a,&b); printf("%d\n",(int)(1ll*inv2*(solve(n)-1+mod)%mod)); } return 0; }






    推荐阅读