Description
給定n,m,求 ∑ni=1∑mj=1φ(i∗j) mod 1e9+7
n<=100,000,m<=1,000,000,000
Solution
以為這題是之前那道多校的原題,高興地去刷了,然後,兩天過去了。。
n 那麼小顯然暴力枚舉即可
同樣的,我們定義sum(n,m)表示 ∑mi=1φ(i∗n)
然而這裡并不是 free square number
不要緊,顯然如果有個因數 p 有>1的幂,則 sum(n,m)=pαs−1sum(n/(pαs−1))
那麼一開始我們來一發質因數分解即可
然後同樣的, sum(n,m)=φ(p)∗sum(np,m)+sum(n,⌊mp⌋)
然後突然發現 m TM那麼大。。遞歸結尾傳回什麼。。
暴力搞肯定不行了,突然想到有種東西叫杜教篩
趕快拿起來研究了一下
就然我們來推導一下φ的字首和 ϕ 吧
ϕ(n)=∑1=1nφ(i)
又 ∑d|iφ(d)=n
=>φ(n)=n−∑d|n,d<nφ(d)
帶入原式可得
ϕ(n)=∑i=1ni−∑d|i,d<iφ(d)
=n∗(n+1)2−∑d|i,d<iφ(d)
換一下枚舉順序,枚舉i/d可得
ϕ(n)=n∗(n+1)2−∑i=2n∑i=1⌊ni⌋
=n∗(n+1)2−∑i=2nϕ(⌊ni⌋)
網上有個小哥蠻逗,他杜教篩的時候暴力for後面的東西,然後吐槽說常數有點大。。
事實上大家都會按值域搞一發即可
複雜度懶得證明,直接拿來用吧如果預處理前面的項 O(n23)
接下來是精髓,卡常數的部分
照理說這樣已經可以搞出來了,然而我在常數的坑裡徘徊了n久
一開始我在sum的時候套了個map,然後MLE了5次(當然那時樣例跑了20S+)
後來發現去掉map不僅時間變成了10S-而且不會MLE了。。
然後我在杜教篩的時候記搜中把map用手寫hash_table,又快了2S
現在的時間變成了8S整
我從網上随手拔了個AC代碼,親測7.6S,交一發,A了。。
于是我就各種不爽。
我在篩素數的時候把每個數字的最大因數給篩出來,然後看似可以省掉一個 n√ ?然而親測沒有效果。。
為什麼那個代碼同樣在求sum的時候套了個map,為什麼不好會MLE,而且可以跑到7.6S,遙(gui)遙(ce)領(ping)先(qi)
我再三反思,一定是輾轉相除的次數太多了,調用遞歸的次數太多了。。
完了多半是廢了。。
這時候我突然發現一開始的那個帶進去算sum的東西因為已經因式分解了,是以值域很窄?
無腦套了個記憶化
跑出了3S驚人的速度!!!
欣慰地笑了,感覺自己可以卡世界上一切常數了
Code
/**************************************************************
Problem: 3512
User: bblss123
Language: C++
Result: Accepted
Time:8292 ms
Memory:91760 kb
****************************************************************/
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int M=+;
const int P=+;
typedef long long ll;
inline void Mod_add(int &a,int b){
if((a+=b)>=P)a-=P;
}
inline int Mod(int x){
if(x>=P)return x-P;
if(x<)return x+P;
return x;
}
int prime[M>>],phi[M],s[M],sf[M],t=;
inline void Eular(){
phi[]=;
for(int i=;i<M;++i){
if(!sf[i])prime[t++]=i,phi[i]=i-,sf[i]=i;
for(int j=,p;j<t&&l*(p=prime[j])*i<M;++j){
sf[i*p]=p;
if(i%p==){phi[i*p]=p*phi[i];break;}
phi[i*p]=phi[i]*(p-);
}
}
for(int i=;i<M;++i)
s[i]=Mod(s[i-]+phi[i]);
}
struct hash_table{
int tot,head[];
struct Edge{
int s,c,nxt;
}edge[];
inline hash_table(){memset(head,-,sizeof(head));}
inline void add(int w,int &x,int &c){
edge[tot]=(Edge){x,c,head[w]};
head[w]=tot++;
}
inline int find(int &x){
int w=x>>;
for(int i=head[w];~i;i=edge[i].nxt){
int s=edge[i].s;
if(s==x)return edge[i].c;
}
return -;
}
inline void insert(int &x,int &val){
add(x>>,x,val);
}
}mp;
inline int verphi(int x){
if(x<M)return s[x];
int w=mp.find(x);
if(~w)return w;
int k=;
for(int i=,s;i<=x;++i){
s=x/(x/i);
Mod_add(k,(ll)(s-i+)*verphi(x/i)%P);
i=s;
}
k=(((ll)x*(x+)>>)-k)%P;
mp.insert(x,k);
return k;
}
inline int Divide(int x){
int res=;
for(int i=;prime[i]*prime[i]<=x;++i)
if(x%prime[i]==){
int c=;
for(;x%prime[i]==;++c)x/=prime[i];
for(--c;c--;)res*=prime[i];
}
return res;
}
inline int sum(int n,int m){
if(!m)return ;
if(m==)return phi[n];
if(n==)return verphi(m);
int &k=sf[n];
return ((ll)sum(n/k,m)*(k-)+sum(n,m/k))%P;
}
int f[];
int main(){
Eular();
int n,m,ans=;
scanf("%d %d",&n,&m);
memset(f,-,sizeof(f));
for(int i=;i<=n;++i){
ll k=Divide(i),t=i/k;
if(~f[t])Mod_add(ans,k*f[t]%P);
else Mod_add(ans,k*(f[t]=sum(t,m))%P);
}
printf("%d\n",ans);
return ;
}