• 【YY的GCD】


    [f(n)=sum_{i=1}^Nsum_{j=1}^M[(i,j)=n] ]

    我们的答案显然是

    [ans=sum_{pin prime}f(p) ]

    [F(n)=sum_{i=1}^Nsum_{j=1}^M[n|(i,j)] ]

    即有多少个数对的最大公约数是(n)的倍数

    显然(F(n)=left lfloor frac{N}{n} ight floor imesleft lfloor frac{M}{n} ight floor)

    同时还存在

    [F(n)=sum_{n|d}f(d) ]

    看起来并不能反演,但是我们大胆猜测会存在这样的性质

    [f(n)=sum_{n|d}mu(frac{d}{n})F(d) ]

    看起来很靠谱啊,那就证明一下吧

    [sum_{n|d}mu(frac{d}{n})F(d)=sum_{n|d}mu(frac{d}{n})sum_{d|i}f(i) ]

    考虑把(f(i))提前,因为(n|d,d|i),所以(n|i)

    [sum_{n|d}mu(frac{d}{n})sum_{d|i}f(i)=sum_{n|i}f(i)sum_{d|i,n|d}mu(frac{d}{n}) ]

    (d=kn,i=cd),则有(i=ckn)

    (frac{d}{n}=k,frac{i}{n}=ck),所以其实是在(frac{i}{n})的约数

    所以可以写成

    [sum_{n|i}f(i)sum_{d|i,n|d}mu(frac{d}{n})=sum_{n|i}f(i)sum_{d|frac{i}{n}}mu(d) ]

    所以只有在(i=n)的时候(sum_{d|frac{i}{n}}mu(d)=1),所以这个柿子的值是成立的

    所以有一种新的反演形式

    [F(n)=sum_{n|d}f(d) ]

    就有

    [f(n)=sum_{n|d}mu(frac{d}{n})F(d) ]

    之后我们的柿子变成了

    [ans=sum_{pin prime}sum_{n|p}mu(frac{p}{n})F(n)=sum_{pin prime}sum_{n|p}mu(frac{p}{n}) imes left lfloor frac{N}{n} ight floor imesleft lfloor frac{M}{n} ight floor ]

    于是现在得到了一个复杂度非常玄学的做法,就是枚举(p)之后枚举(p)的倍数

    暴力就写好了

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define re register
    #define maxn 10000005
    #define LL long long
    #define min(a,b) ((a)<(b)?(a):(b))
    #define max(a,b) ((a)>(b)?(a):(b))
    inline int read()
    {
    	char c=getchar();
    	int x=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c>='0'&&c<='9')
    		x=(x<<3)+(x<<1)+c-48,c=getchar();
    	return x;
    }
    int mu[maxn],p[maxn],f[maxn];
    int n,m,T;
    int main()
    {
    	scanf("%d",&T);
    	f[1]=mu[1]=1;
    	for(re int i=2;i<=10000000;i++)
    	{
    		if(!f[i]) p[++p[0]]=i,mu[i]=-1;
    		for(re int j=1;j<=p[0]&&p[j]*i<=10000000;j++)
    		{
    			f[p[j]*i]=1;
    			if(i%p[j]==0) break;
    			mu[p[j]*i]=-1*mu[i];
    		}
    	}
    	while(T--)
    	{
    		scanf("%d%d",&n,&m);
    		LL ans=0;
    		for(re int i=1;i<=p[0]&&p[i]<=min(n,m);i++)
    		{
    			for(re int j=1;j*p[i]<=min(n,m);j++)
    				ans+=mu[j]*(n/(j*p[i]))*(m/(j*p[i]));
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
    

    把柿子化到这里显然还不够啊

    我们需要继续搞一搞

    (k imes p=n)

    那么

    [sum_{pin prime}sum_{n|p}mu(frac{p}{n}) imes left lfloor frac{N}{n} ight floor imesleft lfloor frac{M}{n} ight floor=sum_{pin prime}sum_{k=1}^{left lfloorfrac{N}{p} ight floor}mu(k) imes left lfloor frac{N}{kp} ight floor imesleft lfloor frac{M}{kp} ight floor ]

    (T=kp)

    于是就有

    [sum_{pin prime}sum_{k=1}^{left lfloorfrac{N}{p} ight floor}mu(k) imes left lfloor frac{N}{kp} ight floor imesleft lfloor frac{M}{kp} ight floor=sum_{T=1}^{N}sum_{t|T,tin prime}mu(frac{T}{t}) imes left lfloor frac{N}{T} ight floor imesleft lfloor frac{M}{T} ight floor ]

    [=sum_{T=1}^{N} left lfloor frac{N}{T} ight floor imesleft lfloor frac{M}{T} ight floorsum_{t|T,tin prime}mu(frac{T}{t}) ]

    发现好像前面那两个东西可以两个整除分块一起上,后面这个(sum_{t|T,tin prime}mu(frac{T}{t}))看起来好像需要一个前缀和

    于是就可以啦

    代码

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define re register
    #define maxn 10000005
    #define LL long long
    #define min(a,b) ((a)<(b)?(a):(b))
    #define max(a,b) ((a)>(b)?(a):(b))
    inline int read()
    {
    	char c=getchar();
    	int x=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c>='0'&&c<='9')
    		x=(x<<3)+(x<<1)+c-48,c=getchar();
    	return x;
    }
    int mu[maxn],f[maxn],p[maxn>>2];
    LL pre[maxn];
    int T,n,m;
    int main()
    {
    	f[1]=mu[1]=1;
    	for(re int i=2;i<=10000000;i++)
    	{
    		if(!f[i]) p[++p[0]]=i,mu[i]=-1;
    		for(re int j=1;j<=p[0]&&p[j]*i<=10000000;j++)
    		{
    			f[p[j]*i]=1;
    			if(i%p[j]==0) break;
    			mu[p[j]*i]=-1*mu[i];
    		}
    	}
    	for(re int i=1;i<=p[0];i++)
    		for(re int j=1;j*p[i]<=10000000;j++) pre[j*p[i]]+=mu[j];
    	for(re int i=1;i<=10000000;i++) pre[i]+=pre[i-1];
    	scanf("%d",&T);
    	while(T--)
    	{
    		LL ans=0;
    		n=read(),m=read();
    		if(n>m) std::swap(n,m);
    		for(re int l=1,r;l<=n;l=r+1)
    		{
    			r=min(n/(n/l),m/(m/l));
    			ans+=(LL)(n/l)*(m/l)*(pre[r]-pre[l-1]);
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    【转】 VC MFC 钩子 实现 自绘 窗体 标题栏 非客户区
    Scintilla开源库使用指南(二)
    Scintilla开源库使用指南(一)
    【转】MFC 多文档
    多视图识别
    获得MFC窗口其它类指针的方法
    sql2005 查看数据库或表大小的系统存储过程 sp_spaceused
    哪里是乐土?关于团队良性循环 (转)
    项目管理反思——前言
    项目经理思考——团队
  • 原文地址:https://www.cnblogs.com/asuldb/p/10205681.html
Copyright © 2020-2023  润新知