数论-整除分块

好久没碰数论的东西了,已经完全生疏了。

做了几道数论题总结一下。

余数求和

大意:
求 \sum_{i=1}^{n}k%i

思路:
隐约记得哪次比赛做过原题,也做出来了,但是现在还是有点懵了。。。

先把式子转化一下吧

\sum_{i=1}^{n}k%i= \sum_{i=1}^{n}k-\left \lfloor k/i \right \rfloor\doteq n*k-\sum_{i=1}^{n}i*\left \lfloor k/i \right \rfloor

那么整个式子就稍微有点样子了。

接下来对每一个l求对应的r

t=\left \lfloor k/l \right \rfloor

r=max(i),i*t<=n;

得到:

r=k/t;

这是对于每一个l对应的r,对于\sum里面的i*\left \lfloor k/i \right \rfloor,i在连续的区间里是一个等差数列,那么直接套公式即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mx *max_element 
const ll N=2e5+10;
ll n,k,r;
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>k;
	ll ans=n*k;
	for(int l=1;l<=n;l=r+1)
	{
		ll t=k/l;
		if(t!=0) r=min(k/t,n);
		else r=n;
		ans-=t*(r-l+1)*(l+r)/2;
	}
	cout<<ans<<endl;
	return 0;
}

虽然是一道省选-,但代码是真的短。

calculating

大意:

思路:
首先,这个\prod_{i=1}^{n}ki就是求\sigma (n),也就是我们所熟知的求数字约数的函数,换句话说,这里f(x)=\sum_{i=1}^{n}\sum_{d|i}^{}1,其中\sum_{d|i}1=\sigma (i).

不妨做一个转换,改为先枚举d,则

f(x)=\sum_{i=1}^{n}\sum_{d|i}^{}1=\sum_{d=1}^{n}\left \lfloor n/d \right \rfloor,

到这一步,要求f(x)那就是轻而易举了。

最后,由于i是从l到r,那么\sum_{i=l}^{r}f(x)=\sum_{i=1}^{r}f(x)-\sum_{i=1}^{l-1}f(x),这样的话写一个函数就好了。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=998244353;
ll a,b;
ll f(ll x)
{
	ll ans=0;
	ll l,r;
	for(ll l=1;l<=x;l=r+1)
	{
		r=x/(x/l);
		ans=(ans+(x/l)*(r-l+1)%mod)%mod;
	}
	return ans%mod;
}
int main()
{
	cin>>a>>b;
	cout<<(f(b)-f(a-1)+mod)%mod<<endl;
	return 0;
}

点是有点多,但都想到了就挺板的。

模积和

大意:

思路:
这里有一个限制是i!=j,所以不妨把式子转化一下。

\sum_{i=1}^{n}\sum_{j=1}^{m}n%i*m%j(i\neq j)=\sum_{i=1}^{n}\sum_{j=1}^{m}(n%i)*(m%j)-\sum_{i=1}^{n}(n%i)*(m%i)

 其中\sum_{i=1}^{n}\sum_{j=1}^{m}(n%i)*(m%j)=\sum_{i=1}^{n}n%i*\sum_{j=1}^{m}m%j

这个就是我们在第一个问题里讨论的东西,那应该就很好处理了。

然后是后面的东西,如果展开的话:

\sum_{i=1}^{n}(n%i)*(m%i)=\sum_{i=1}^{n}(n-i*\left \lfloor n/i \right \rfloor)*(m-i*\left \lfloor m/i \right \rfloor)

=\sum_{i=1}^{n}n*m-m*i*\left \lfloor n/i \right \rfloor-n*i*\left \lfloor m/i \right \rfloor+i*i*\left \lfloor n/i \right \rfloor*\left \lfloor m/i \right \rfloor

这里的第一项就不用说了,第二三项其实跟上面讨论的\sum_{i=1}^{n}n%i=\sum_{i=1}^{n}n-i*\left \lfloor n/i \right \rfloor是一个道理,只是加了一个常数而已。

那么最后一项,i*i*\left \lfloor n/i \right \rfloor*\left \lfloor m/i \right \rfloor,与上一个的区别只是i的次数提高了一项,但如果照搬之前的思路,求等差数列的和的话,\sum i^{^{2}}=n*(n+1)*(2n+1)/6,那么这一项也解决了,那么整个题目到这里也就ok了。

还想说一个坑点,就是题目里这个取模的数居然不是质数。。。

也怪我傻,998244353,1e9+7见多了,看啥都像质数

所以这里的逆元得自己提前去求好了,不能用快速幂。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=19940417;
const ll inv2=9970209;
const ll inv6=3323403;
#define endl '\n'
ll n,m;
ll sd(ll x)
{
	return x*(x+1)%mod*(2*x+1)%mod*inv6%mod;
}
ll sdd(ll x)
{
	return x*(x+1)%mod*inv2%mod;
}
ll f(ll n)//求sum(n-(n/i)*i) 
{
	ll sum=n*n%mod;
	//cout<<sum<<endl;
	for(ll l=1,r;l<=n;l=r+1)
	{
	    ll t=n/l;
	    r=n/t;
	    sum=(sum-t*(sdd(r)-sdd(l-1)+mod)%mod+mod)%mod;
	    //cout<<sum<<endl;
	} 
	return sum;
}
int main()
{
	cin>>n>>m;
	//cout<<sdd(4)<<' '<<sdd(3)<<endl;
	//cout<<f(n)<<" "<<f(m)<<endl;
	ll sum=0,ans=0;
	sum=(sum+f(n))%mod;
	sum=(sum*f(m))%mod;
	ans=sum;sum=0;
	for(ll l=1,r;l<=min(n,m);l=r+1)
	{
		r=min(n/(n/l),m/(m/l));
		sum+=(r-l+1+mod)%mod*n%mod*m%mod;
		sum-=(r-l+1+mod)%mod*(l+r)%mod*((n/l)*m%mod+(m/l)*n%mod)%mod*inv2%mod;
		sum+=(sd(r)-sd(l-1)+mod)%mod*(n/l)%mod*(m/l)%mod;
		sum=(sum+mod)%mod; 
	}
	cout<<(ans-sum+mod)%mod<<endl;
	return 0;
}

大致就先这样,后面可能会补新的题 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值