bzoj_2154: Crash的数字表格 (自己想了70分)

有两个注意的地方:

1.预处理到max(n,m)max(n,m)的范围就行了,要不在大视野上超时

2. (x+1)*x/2*(y+1)*y/2 在中间会炸long long,记得在中间mod

自己的n<=1000000做法:

主要的限制是我的因为要除一个东西,没法线性筛,只能log筛,n>2000000就会超时

ans=ni=1mj=1lcm(i,j)ans=ni=1mj=1lcm(i,j)

ans=ni=1mj=1ijgcd(i,j)ans=ni=1mj=1ijgcd(i,j)

这是题意

我的是 设f(i) 为 gcd(x,y)==i的x*y的和  F(i) 为i|gcd(x,y) 的x*y的和

反演后

f(i)=i|dμ(di)F(d)x=niy=miF(i)=i2(x+1)x/2(y+1)y

ans=min(n,m)d=1x(x+1)y(y+1)i|dμ(di)4i

但是渊哥说后面的那个东西分母不是积性函数,不能线性筛..

 

复制代码
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
inline int read()
{
    char q=getchar();int ans=0;
    while(q<'0'||q>'9')q=getchar();
    while(q>='0'&&q<='9'){ans=ans*10+q-'0';q=getchar();}
    return ans;
}
const int mod=20101009;
const int N=2000006;

int prime[N],cnt;
bool he[N];
int mu[N];
ll ni[N],ji[N];

void chu()
{
    ni[1]=1;
    for(int i=2;i<N;++i)
      ni[i]=(ll)(mod-mod/i)*ni[mod%i]%mod;
    
    mu[1]=1;//ji[1]=1;
    for(int i=2;i<N;++i)
    {
        if(!he[i])
        {
            prime[++cnt]=i;
            mu[i]=-1;
            //ji[i]=(ni[i]-1+mod)%mod;
        }
        for(int j=1;j<=cnt&&prime[j]*i<N;++j)
        {
            he[i*prime[j]]=1;
            if(i%prime[j]==0)
            {
                mu[i*prime[j]]=0;
                //ji[i*prime[j]]=ji[i]*ni[prime[j]]%mod;
                break;
            }
            //ji[i*prime[j]]=ji[i]*ji[prime[j]]%mod;
            mu[i*prime[j]]=-mu[i];
        }
    }
    
    for(int i=1;i<N;++i)
      for(int j=i;j<N;j+=i)
        ji[j]=(ji[j]+mu[j/i]*ni[i]%mod*ni[4]%mod*j%mod*j%mod+mod)%mod;
    for(int i=1;i<N;++i)
      ji[i]=((ji[i]+ji[i-1])%mod+mod)%mod;
}

int n,m;

ll work()
{
    if(n>m)
        swap(n,m);
    ll ans=0,tt;
    int nx;
    for(int i=1;i<=n;i=nx+1)
    {
        nx=min( n/(n/i),m/(m/i) );
        tt=(ll)(n/i+1)%mod*(n/i)%mod*(m/i)%mod*(m/i+1)%mod;
        ans=(ans+tt*(ji[nx]-ji[i-1]+mod)%mod)%mod;
    }
    return (ans+mod)%mod;
}

int main(){
    
    freopen("in.in","r",stdin);
    //freopen("nt2011_table.in","r",stdin);
    //freopen("nt2011_table.out","w",stdout);
    
    chu();
    scanf("%d%d",&n,&m);
    cout<<work();
}
    
自己的
复制代码

 

 

 

然后我就怂题解了

正解:

ans=ni=1mj=1ijgcd(i,j)

ans=min(n,m)d=1d2f(nd,md)d

f(x,y) 为gcd(x,y)==1的x*y的和,反演后

f(x,y)=min(n,m)d=1d2μ(d)Sum(xd,yd)

Sum(x,y)=(x+1)x/2(y+1)y/2

然后就是O(nn)

用到的思想就是把求gcd(x,y)==d转化成了求

复制代码
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
inline int read()
{
    char q=getchar();int ans=0;
    while(q<'0'||q>'9')q=getchar();
    while(q>='0'&&q<='9'){ans=ans*10+q-'0';q=getchar();}
    return ans;
}
const int mod=20101009;
const int N=10000006;

int prime[N],cnt;
bool he[N];
int mu[N];
ll pr1[N],pr2[N];
int n,m;

void chu()
{
    int q1=max(n,m);
    
    mu[1]=1;
    for(int i=2;i<=q1;++i)
    {
        if(!he[i])
        {
            prime[++cnt]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=cnt&&prime[j]*i<=q1;++j)
        {
            he[i*prime[j]]=1;
            if(i%prime[j]==0)
            {
                mu[i*prime[j]]=0;
                break;
            }
            mu[i*prime[j]]=-mu[i];
        }
    }
    
    for(int i=1;i<=q1;++i)
    {
      pr1[i]=(pr1[i-1]+i)%mod;
      pr2[i]=(pr2[i-1]+((ll)mu[i]*i%mod*i%mod)+mod)%mod;
    }
}

ll sum(ll x,ll y)
{
    return ( ((x+1)*x/2%mod) * ((y+1)*y/2%mod) )%mod;
}

ll f(int x,int y)
{
    if(x>y)
        swap(x,y);
    ll ans=0;
    int nx;
    for(int i=1;i<=x;i=nx+1)
    {
        nx=min( x/(x/i),y/(y/i) );
        ans=(ans+sum(x/i,y/i)*(pr2[nx]-pr2[i-1]+mod)%mod)%mod;
        //nx=min( x/(x/i),y/(y/i) );
        //ans=(ans+((x/i+1)*(x/i)/2)*((y/i+1)*(y/i)/2)%mod*(pr2[nx]-pr2[i-1]+mod)%mod)%mod;
    }
    return (ans+mod)%mod;
}

ll work()
{
    if(n>m)
        swap(n,m);
    ll ans=0;
    int nx;
    for(int i=1;i<=n;i=nx+1)
    {
        nx=min( n/(n/i),m/(m/i) );
        ans=(ans+(pr1[nx]-pr1[i-1]+mod)%mod*f(n/i,m/i))%mod;
    }
    return (ans+mod)%mod;
}

int main(){
    
    //freopen("in.in","r",stdin);
    //freopen("nt2011_table.in","r",stdin);
    //freopen("nt2011_table.out","w",stdout);
    
    scanf("%d%d",&n,&m);
    chu();
    cout<<work();
}
    
正解
复制代码

 

posted @   A_LEAF  阅读(116)  评论(0编辑  收藏  举报
编辑推荐:
· 你所不知道的 C/C++ 宏知识
· 聊一聊 操作系统蓝屏 c0000102 的故障分析
· SQL Server 内存占用高分析
· .NET Core GC计划阶段(plan_phase)底层原理浅谈
· .NET开发智能桌面机器人:用.NET IoT库编写驱动控制两个屏幕
阅读排行:
· 我干了两个月的大项目,开源了!
· 推荐一款非常好用的在线 SSH 管理工具
· 千万级的大表,如何做性能调优?
· 盘点!HelloGitHub 年度热门开源项目
· Phi小模型开发教程:用C#开发本地部署AI聊天工具,只需CPU,不需要GPU,3G内存就可以运行,
点击右上角即可分享
微信分享提示