zoukankan      html  css  js  c++  java
  • 【NOI2010】能量采集

    题意简述

    求:

    [2sum_{i=1}^nsum_{j=1}^m(gcd(i,j)-1)=-nm+2sum_{i=1}^nsum_{j=1}^mgcd(i,j) ]

    题解

    先说一个有点巧妙的变化,等会要用到(下面的k是给出的定值)。
    首先有一个简单的结论:

    [epsilon(n)=sum_{d|n}mu(d) ]

    也可表示为狄利克雷卷积的形式:

    [epsilon=mu*1 ]

    由此得出这个变化:

    [egin{align*} sum_{i=1}^nsum_{j=1}^m [gcd(i, j)=1]&=sum_{i=1}^nsum_{j=1}^m sum_{d|gcd(i,j)}mu(d)\ &=sum_{i=1}^nsum_{j=1}^m sum_{d|i,d|j}mu(d)\ &=sum_{d=1}^{min(n, m)}mu(d)lfloorfrac{n}{d} floorlfloorfrac{m}{d} floor\ end{align*}]

    上面的最后一步是将枚举(i,j)转换为枚举其公因数(d),再由一个数(d)(1sim n)中的倍数有(lfloorfrac{n}{d} floor)个转化得。

    还有一个关于取整函数的性质:

    [lfloorlfloorfrac{x}{a} floor/b floor=lfloorfrac{x}{ab} floor ]

    所以接下来就可以这样推式子了:

    [egin{align*} sum_{i=1}^nsum_{j=1}^mgcd(i,j)& =sum_{d=1}^{min(n,m)}dsum_{i=1}^{lfloorfrac{n}{d} floor}sum_{j=1}^{lfloorfrac{m}{d} floor}[gcd(i, j)=1]\ &=sum_{d=1}^{min(n,m)}dsum_{k=1}^{min(lfloorfrac{n}{d} floor, lfloorfrac{m}{d} floor)}mu(k)lfloorfrac{n}{kd} floorlfloorfrac{m}{kd} floor\ end{align*}]

    枚举(d),然后后面依据(lfloorfrac{n}{kd} floorlfloorfrac{m}{kd} floor)的取值,相同取值的区间合并,答案加上相应取值与区间和的乘积,即做一个数论分块,本题就可到此结束。

    代码

    #include <cstdio>
    #include <cctype>
    typedef long long ll;
    const int maxn=1e5+10;
    int prim[maxn],mu[maxn],s[maxn];
    int tot;
    bool vis[maxn];
    
    int min(int x,int y) {return x<y?x:y;}
    void swap(int &x,int &y) {x^=y^=x^=y;}
    void prework(int n)
    {
    	vis[0]=vis[1]=1;
    	mu[1]=s[1]=1;
    	for (int i=2;i<=n;i++)
    	{
    		if (!vis[i])
    			prim[++tot]=i,mu[i]=-1;
    		s[i]=s[i-1]+mu[i];
    		for (int j=1;j<=tot&&(ll)i*prim[j]<=n;j++)
    		{
    			vis[i*prim[j]]=1;
    			if (i%prim[j]==0)
    			{
    				mu[i*prim[j]]=0;
    				break;
    			}
    			mu[i*prim[j]]=-mu[i];
    		}
    	}
    }
    int read()
    {
    	int res=0;
    	char ch=getchar();
    	while(!isdigit(ch))
    		ch=getchar();
    	while(isdigit(ch))
    		res=res*10+ch-'0',ch=getchar();
    	return res;
    }
    int main()
    {
    	int n=read(),m=read();
    	ll ans=0;
    	if (n>m)
    		swap(n, m);
    	prework(n);
    	for (int d=1;d<=n;d++)
    	{
    		for (int l=1,r=1;l<=n/d;l=r+1)
    		{
    			r=min(n/d/(n/d/l), m/d/(m/d/l));
    			ans+=(ll)d*(s[r]-s[l-1])*(n/d/l)*(m/d/l);
    		}
    	}
    	printf("%lld
    ",ans*2-(ll)n*m);
    	return 0;
    }
    
  • 相关阅读:
    vue——学习笔记
    工作笔记——上传图片
    前端调试利器——BrowserSync
    工作笔记——一些常用函数的封装
    【html、CSS、javascript-3】几个基本元素
    【html、CSS、javascript-2】CSS基础
    【html、CSS、javascript-1】html基础
    【python之路37】with上下文管理
    【python之路36】进程、线程、协程相关
    【python之路35】FTP文件断电续传作业
  • 原文地址:https://www.cnblogs.com/hkr04/p/NOI2010-energy.html
Copyright © 2011-2022 走看看