分析矿洞 题解
zhoutb2333 · · 个人记录
暴力枚举激光的预期得分为
观察发现每个点
其实要求的就是这个:
直接暴力求期望得分
考虑反演:
直接分块做可以到
令
这样我们得到
这样就可以递归往下筛了。
线性筛预处理的范围的话,在
好像有点卡常,调高了点时限,注意
更坑的是
代码:
#include<bits/stdc++.h>
#define maxn 5000010
#define ll long long
using namespace std;
int pri[maxn],phi[maxn],pricnt=0;
ll sphi[maxn],s2[maxn],n,inv2,inv6;
const ll p=1e9+7;
bool notpri[maxn];
map<ll,ll> mp,mp2;
ll pw(ll a,ll b){
ll ret=1;
while(b){
if(b&1)
ret=ret*a%p;
a=a*a%p;
b>>=1;
}
return ret;
}
void init(){
phi[1]=1;
for(int i=2;i<maxn;i++){
if(!notpri[i]){
pri[++pricnt]=i;
phi[i]=i-1;
}
for(int j=1;j<=pricnt;j++){
if(i*pri[j]>=maxn)
break;
notpri[i*pri[j]]=true;
if(i%pri[j]==0){
phi[i*pri[j]]=phi[i]*pri[j];
break;
}
phi[i*pri[j]]=phi[i]*(pri[j]-1);
}
}
for(int i=1;i<maxn;i++)
sphi[i]=(sphi[i-1]+phi[i])%p,s2[i]=(s2[i-1]+1LL*i*phi[i])%p;
inv2=pw(2,p-2),inv6=pw(6,p-2);
}
ll c1(ll x){
x%=p;
return 1LL*x*(x+1)/2%p;
}
ll c2(ll x){
x%=p;
return 1LL*x*(x+1)%p*(2*x+1)%p*inv6%p;
}
ll _phi(ll x){
if(x<maxn)
return sphi[x];
if(mp[x])
return mp[x];
ll pos,ret=c1(x);
for(ll i=2;i<=x;i=pos+1){
pos=x/(x/i);
(ret-=1LL*(pos-i+1)%p*(_phi(x/i)%p))%=p;
}
return mp[x]=(ret+p)%p;
}
ll calc(ll x){
if(x<maxn)
return s2[x];
if(mp2[x])
return mp2[x];
ll pos,ret=c2(x);
for(ll i=2;i<=x;i=pos+1){
pos=x/(x/i);
(ret-=1LL*(pos+i)%p*((pos-i+1)%p*inv2%p)%p*calc(x/i))%=p;
}
return mp2[x]=(ret+p)%p;
}
ll solve(){
ll pos,ret=0;
for(ll i=1;i<=n;i=pos+1){
pos=n/(n/i);
(ret+=1LL*(calc(pos)-calc(i-1)+p)*(2*_phi(n/i)-1+p)%p)%=p;
}
return (ret+p)%p;
}
int main(){
scanf("%lld",&n);
init();
printf("%lld\n",solve());
return 0;
}