佳木斯集训Day2例题 数论

由于今天良心学长的题解非常详细还附带标程 以及我答的特别水 所以今天做例题的博客…
今天的专题是数论----欧拉函数、欧拉定理、费马小定理、中国剩余定理
一想起要打公式就头皮发麻…所以我有可能粘图片

Gcd(bzoj2818)

fzoj4284
题目描述
给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的数对(x,y)有多少对.
输入
一个整数N
输出
如题
样例输入
4
样例输出
4
显然我们需要枚举gcd(x,y)gcd(x,y)gcd(x,y),对于每个素数p=gcd(x,y)p=gcd(x,y)p=gcd(x,y) 对答案的贡献为111⌊np⌋\left \lfloor \frac{n} {p} \right \rfloorpn的有序互质对(x,y)(x,y)(x,y)的个数
y=xy=xy=x时 有且仅有y=x=1y=x=1y=x=1
y>xy>xy>x时 有序互质对(x,y)(x,y)(x,y)的个数为小于y与y互质的数的个数,即y的欧拉函数Φ(y)\Phi(y)Φ(y)
同理,当x>y时 有序互质对(x,y)(x,y)(x,y)Φ(x)\Phi(x)Φ(x)
所以ans=∑p∈P2∑i=1⌊np⌋CΦ(i)−1ans=\sum_{p\in P} 2\sum_{i=1}^{\left \lfloor \frac{n} {p} \right \rfloor}C\Phi(i) -1ans=pP2i=1pnCΦ(i)1
只需线性筛欧拉函数 再求出前缀和就ojbkk了

#include<cstdio>
unsigned long long ans,s[10000001];
int n,cnt,euc[10000001],prime[10000001];
bool vis[10000001];
int main()
{
    scanf("%d",&n);
    euc[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!vis[i])euc[i]=i-1,prime[++cnt]=i;
        for(int j=1;j<=cnt;j++)
        {
            if(i*prime[j]>n)break;
            vis[i*prime[j]]=true;
            if(i%prime[j]==0)
            {
                euc[i*prime[j]]=euc[i]*prime[j];
                break;
            }
            else euc[i*prime[j]]=euc[i]*euc[prime[j]];
        }
    }
    for(int i=1;i<=n;i++)s[i]=s[i-1]+euc[i];
    for(int i=1;i<=cnt;i++)ans+=s[n/prime[i]]*2-1;
    printf("%llu\n",ans);
}

欧拉心算(bzoj4804)

fzoj5383

这道题要用到数论分块
首先什么是数论分块 顾名思义就是分块在数论上的应用
不过非常神奇的一点是你如果不会分块但你还是可以会数论分块的
先看一道题理解一下数论分块

题目描述
给出正整数n和k,计算j(n, k)=k mod 1 + k mod 2 + k mod 3 + … + k mod n的值,其中k mod i表示k除以i的余数。例如j(5, 3)=3 mod 1 + 3 mod 2 + 3 mod 3 + 3 mod 4 + 3 mod 5=0+1+0+3+3=7
输入
输入仅一行,包含两个整数n, k。
输出
输出仅一行,即j(n, k)
样例输入
5 3
样例输出
7
所求为
∑i=1nkmodi\sum_{i=1}^{n}k mod ii=1nkmodi
为了应用数论分块 将这个式子改为:
∑i=1nk−i×(⌊ki⌋)\sum_{i=1}^{n}k-i×(\left \lfloor \frac{k} {i} \right \rfloor)i=1nki×(ik)
前面的n×kn×kn×k直接先算出来
后面的iii是单增的
⌊ki⌋\left \lfloor \frac{k} {i} \right \rfloorik在一段范围内是相等的 总共只有2n种取值2\sqrt{n}种取值2n
因此,每次求出这一段范围,直接计算等差数列即可
代码如下:

#include<cstdio>
inline int read()
{
    int x=0,t=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
long long ans,n,k;
int main()
{
    n=read();k=read();
    ans=n*k;
    for(long long l=1,r=0;l<=n;l=r+1)
    {
        if(k/l)r=min(n,k/(k/l));
        else r=n;
        ans-=(k/l)*(r-l+1)*(l+r)>>1;
    }
    
    printf("%lld\n",ans);
    return 0;
}

好了偏题了 大家应该对数论分块有理解了 那么我们回到正题
题目描述
给出一个数字N,求
在这里插入图片描述
输入
第一行为一个正整数T,表示数据组数。
接下来T行为询问,每行包含一个正整数N。
T<=5000,N<=10^7
输出
如题
样例输入
4
样例输出
4
一步一步的化简到这里
在这里插入图片描述
最后一行减号左边用数论分块
代码如下:

#include<cstdio>
unsigned long long ans,s[10000001];
int n,t,cnt,lst,euc[10000001],prime[10000001];
bool vis[10000001];
int main()
{
	n=10000000;
	euc[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])euc[i]=i-1,prime[++cnt]=i;
		for(int j=1;j<=cnt;j++)
		{
			if(i*prime[j]>n)break;
			vis[i*prime[j]]=true;
			if(i%prime[j]==0)
			{
				euc[i*prime[j]]=euc[i]*prime[j];
				break;
			}
			else euc[i*prime[j]]=euc[i]*euc[prime[j]];
		}
	}
	for(int i=1;i<=n;i++)s[i]=s[i-1]+euc[i];
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		ans=lst=0;
		for(int i=1;i<=n;i=lst+1)
		{
			lst=n/(n/i);
			ans+=(s[lst]-s[i-1])*s[n/i];
		}
		printf("%llu\n",2*ans-s[n]);
	}
}

今天先发两个吧 后面的我会补上去的
有问题可以发在评论区或者加QQ407694747QQ407694747QQ407694747一起讨论
各位大佬各路神犇请多多指教

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值