题解 P1829 【[国家集训队]Crash的数字表格 / JZPTAB】
看到没有写杜教筛的十分开心.
我会求
首先一定可以套上这里的做法.
不过还是来推一遍(跳跃性可能略大,详细步骤请看上面的链接).以下除法均向下取整.不失一般性,设
设
如果我们再设一个
原式可以写成
总的复杂度其实是
但是要预处理
回到设
令
原式
那么我们只需要预处理
怎么预处理呢?显然可以直接
先把
void make(int n)
{
f[1]=p[1]=1;
for(int i=2;i<=n;i++)
{
if(!p[i])prime[++cnt]=i,f[i]=i*(1LL-i);
for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
{
int x=i*prime[j];p[x]=1;
if(i%prime[j])f[x]=f[i]*prime[j]*(1-prime[j]);
else f[x]=f[i]*prime[j];
}
}
for(int i=2;i<=n;i++)f[i]=(f[i]+f[i-1])%mod;//处理一个前缀和
}
现在是
单组询问,
我刚才看到某个神仙写完
显然杜教筛了.设
换一换形式我们得到
我们来试试找一个
杜教筛就好啦.
卡了卡常数,现在是换新评测姬之后跑的最快的了(
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define mod 20101009
#define INF 1000000000000000000LL
#define inv2 10050505
#define inv6 16750841
using namespace std;
const int N=1e7+10;
int p[N],prime[N],cnt,mu[N],N2;
long long f[N],n,m;
struct Hash_map
{
struct pair{long long u,v;}val[N];
int fst[N],nxt[N],node_cnt;
long long find(long long n)
{
int key=n&8388607;//秘技·手写哈希表·模数2的幂
for(int i=fst[key];i;i=nxt[i])if(val[i].u==n)return val[i].v;
return 1e18;
}
void ins(long long u,long long v)
{
int key=u&8388607;
val[++node_cnt]=(pair){u,v},nxt[node_cnt]=fst[key],fst[key]=node_cnt;
}
}mpf;
void make(int n)
{
f[1]=p[1]=1;
for(int i=2;i<=n;i++)
{
if(!p[i])prime[++cnt]=i,f[i]=i*(1LL-i);
for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
{
int x=i*prime[j];p[x]=1;
if(i%prime[j])f[x]=f[i]*f[prime[j]];
else {f[x]=f[i]*prime[j],mu[x]=0;break;}
}
}
for(int i=2;i<=n;i++)f[i]=(f[i]%mod+f[i-1]+mod)%mod;
}
long long S(long long n){return n*(n+1)%mod*inv2%mod;}
long long S2(long long n){return n*(n+1)%mod*(2*n+1)%mod*inv6%mod;}
long long Sf(long long n)
{
if(n<=N2)return f[n];
long long x=mpf.find(n);
if(x!=INF)return x;
long long ans=S(n);long long lstans=1;//这里记录上一次的答案来避免重复计算.大约能快那么一点点???
for(long long i=2,lt;i<=n;i=lt+1)
{
long long fn=n/i;lt=n/fn;long long tmp=S2(lt);
ans-=(tmp-lstans)*Sf(fn)%mod;
lstans=tmp;
}
ans=(ans%mod+mod)%mod;mpf.ins(n,ans);return ans;
}
int main()
{
scanf("%lld%lld",&n,&m);
if(n>m)swap(n,m);N2=pow(n,0.7)+1;make(N2);
long long ans=0;long long lstans=0;
for(int i=1,lt;i<=n;i=lt+1)
{
int fn=n/i,fm=m/i;lt=min(n/fn,m/fm);
long long tmp=Sf(lt);//同上
ans=(ans+(tmp-lstans)*S(fn)%mod*S(fm)%mod)%mod;
lstans=tmp;
}
cout<<(ans+mod)%mod<<endl;
}
真是快乐的卡常数题