bzoj4652 [Noi2016]循环之美(Mobius反演+杜教筛+Hash表)

版权声明:转载附上原文地址即可~欢迎各位神犇来虐~ https://blog.csdn.net/Icefox_zhx/article/details/80320586

挖了很久的坑qaq
还记得第一次xtx神犇给我们讲这道题,已经过去快整整一年了呢owo

首先怎么算是纯循环小数呢?
因为要求数值不同的数对个数,因此我们只考虑gcd(x,y)==1的数对(x,y)。
什么时候会出现循环呢?当出现相同余数时。那纯循环呢?就要求第一次出现的相同余数为x%y。即只需要满足xklxmody,l>0,其中l就是循环节长度。
又因为(x,y)=1,所以(k,y)=1
因此我们就是要求x=1ny=1m[gcd(x,y)==1,gcd(y,k)==1]
y=1m[gcd(y,k)==1]x=1n[gcd(x,y)==1]
把后一部分Mobius反演一下就是
y=1m[gcd(y,k)==1]d|ynμ(d)nd
改变枚举顺序可以得到:
d=1nμ(d)ndy=1md[gcd(yd,k)==1]
d=1n[gcd(d,k)==1]μ(d)ndy=1md[gcd(y,k)==1]
f(n,k)=i=1n[gcd(i,k)==1]
我们显然有f(n,k)=nkf(k,k)+f(n%k,k)
因此我们可以预处理出O(K)级别的f的前缀和,这样就可以O(1)计算后一部分了,这样总体复杂度就是O(n),可以获得84分。

考虑进一步的优化,记g(n,k)=i=1n[gcd(i,k)==1]μ(i)如果我们可以处理出这个东西得前缀和我们就可以分块算了。

我们先考虑k的一个质因数p,那么k显然可以写成pcq的形式。由于在[1,n]的范围中只有与k互质的才是有效值,那么若(x,k)==1,我们可以得到(x,p)==1且(x,q)==1。于是,我们可以考虑从(x,q)==1的取值中减去x不与p互质的部分,就可以得到(x,k)==1的部分。
剩下的部分去看大神blog吧,懒得搬了qaq
portal
最后g这个函数是可以递归计算的。复杂度O(logkw(k)),w(k)<=4
边界为k=1时函数值就是mu的前缀和,可以杜教筛来搞,n<=1时为n。
总的复杂度大概是O(nw(k)+n2/3)

要写hash表qaq然而我还是跑不过luogu,人称被卡常数。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 1000010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int prime[N>>3],tot=0,a[10],cnt=0,K,h[N],num=0,h1[5][N],num1=0;
ll mu[N],f[2010],ans=0;
bool notprime[N];
struct Hash_Table{
    int key,val,next;
}data[N],data1[N<<3];
inline int hs(int key){
    int x=key%N;
    for(int i=h[x];i;i=data[i].next)
        if(data[i].key==key) return data[i].val;
    return -1;
}
inline int ins(int key,int val){
    int x=key%N;
    data[++num].next=h[x];h[x]=num;data[num].key=key;data[num].val=val;
}
inline int gcd(int x,int y){return y?gcd(y,x%y):x;}
inline void init(){
    notprime[1]=1;mu[1]=1;
    for(int i=2;i<=1e6;++i){
        if(!notprime[i]) prime[++tot]=i,mu[i]=-1;
        for(int j=1;prime[j]*i<=1e6;++j){
            notprime[prime[j]*i]=1;
            if(i%prime[j]==0){
                mu[i*prime[j]]=0;break;
            }mu[i*prime[j]]=-mu[i];
        }
    }for(int i=2;i<=1e6;++i) mu[i]+=mu[i-1];
    for(int i=1;i<=K;++i) if(gcd(K,i)==1) f[i]=1;
    for(int i=1;i<=K;++i) f[i]+=f[i-1];
    for(int i=1;prime[i]<=K;++i) if(K%prime[i]==0) a[++cnt]=prime[i];
}
inline ll calm(int n){return (n/K)*f[K]+f[n%K];}
inline ll calmu(int n){
    if(n<=1e6) return mu[n];
    int res=hs(n);if(res>0) return res;res=1;
    for(int i=2,lst;i<=n;i=lst+1){
        lst=n/(n/i);res-=calmu(n/i)*(lst-i+1);
    }ins(n,res);return res;
}
inline ll g(int n,int k){
    if(!k) return calmu(n);
    if(n<=1) return n;int x=n%N;
    for(int i=h1[k][x];i;i=data1[i].next)
        if(data1[i].key==n) return data1[i].val;
    ll res=g(n,k-1)+g(n/a[k],k);
    data1[++num1].next=h1[k][x];h1[k][x]=num1;data1[num1].key=n;
    data1[num1].val=res;return res;
}
int main(){
//  freopen("a.in","r",stdin);
    int n=read(),m=read();K=read();init();
    for(int i=1,lst;i<=min(n,m);i=lst+1){
        lst=min(n/(n/i),m/(m/i));
        ans+=(g(lst,cnt)-g(i-1,cnt))*(n/i)*calm(m/i);
    }printf("%lld\n",ans);
    return 0;
}

7.3upd:重打了一遍就跑过去了???还贼快???

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 1000003
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int K,mu[N],prime[N>>3],tot=0,f[2010],h1[5][N],num1=0,cnt=0,a[5],num=0,h[N];
bool notprime[N];
struct Hash_table{
    int key,val,next;
}data1[N],data[N];
inline int gcd(int x,int y){return y?gcd(y,x%y):x;}
inline void init(){
    notprime[1]=1;mu[1]=1;
    for(int i=2;i<=N-3;++i){
        if(!notprime[i]) prime[++tot]=i,mu[i]=-1;
        for(int j=1;prime[j]*i<=N-3;++j){
            notprime[prime[j]*i]=1;
            if(i%prime[j]==0){
                mu[i*prime[j]]=0;break;
            }mu[i*prime[j]]=-mu[i];
        }
    }for(int i=1;i<=N-3;++i) mu[i]+=mu[i-1];
    for(int i=1;i<=K;++i) f[i]=gcd(i,K)==1;
    for(int i=1;i<=K;++i) f[i]+=f[i-1];
    for(int i=1;prime[i]<=K;++i) if(K%prime[i]==0) a[++cnt]=prime[i];
}
inline ll F(int x){return x/K*f[K]+f[x%K];}
inline int calmu(int n){
    if(n<=1e6) return mu[n];
    int x=n%N;
    for(int i=h[x];i;i=data[i].next)
        if(data[i].key==n) return data[i].val;
    int res=1;
    for(int i=2,lst;i<=n;i=lst+1){
        lst=n/(n/i);res-=calmu(n/i)*(lst-i+1);
    }data[++num].next=h[x];h[x]=num;data[num].key=n;data[num].val=res;
    return res;
}
inline ll g(int n,int k){
    if(!k) return calmu(n);if(n<=1) return n;
    int x=n%N;
    for(int i=h1[k][x];i;i=data1[i].next)
        if(data1[i].key==n) return data1[i].val;
    int res=g(n,k-1)+g(n/a[k],k);
    data1[++num1].next=h1[k][x];h1[k][x]=num1;data1[num1].key=n;
    data1[num1].val=res;return res;
}
int main(){
//  freopen("a.in","r",stdin);
    int n=read(),m=read();K=read();init();ll ans=0;
    for(int i=1,lst;i<=min(n,m);i=lst+1){
        lst=min(n/(n/i),m/(m/i));
        ans+=(g(lst,cnt)-g(i-1,cnt))*(n/i)*F(m/i);
    }printf("%lld\n",ans);
    return 0;
}
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页