1. 程式人生 > >codeforces 616E Sum of Remainders(數學公式轉化 較難)

codeforces 616E Sum of Remainders(數學公式轉化 較難)

輸入n,m(1<=n,m<=10^13),求n%1+n%2+n%3+……+n%m,資料結果較大,對10^9+7取餘

n%i ==> n-[n/i]*i

原式化為n*m - i=1m[n/i]i

思路1:l=n/(i+1)+1, r=n/i  ==>  n/x(l<=x<=r)的值必定等於i

舉個例子n=20,m=20,現將n開根號了

i=1 ==>  l=11, r=20

i=2 ==>  l=7, r=10

i=3 ==>.  l=r=6

i=4 ==>  l=r=5

那麼還應該求n%1*1,n%2*2,n%3*3,n%4*4

所以程式碼中的las就是標記還剩下幾個沒有取餘

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
using namespace std;
#define ll __int64
const ll mod=1e9+7;
int main()
{
	ll n, m;
	scanf("%I64d%I64d", &n, &m);
	ll ans = (n%mod)*(m%mod)%mod, tmp=0, las=m+1;
	m = min(m, n);
	for(ll i=1; i<=(ll)sqrt(n*1.0); i++)
	{
		ll l=n/(i+1)+1, r=n/i;
		r = min(r, m);
		if(l>r) continue;
		las = min(las, l);
		ll s1=l+r, s2=r-l+1;
		if(s1%2==0) s1/=2;
		else s2/=2;
		s1%=mod, s2%=mod;
		s1 = (s1*s2)%mod;
		s1 = (s1*i)%mod;
		tmp = (tmp + s1)%mod;
	}
	ans = (ans- tmp +mod )% mod;
	for(int i=1; i<las; i++)
	{
		tmp = n/i%mod*i%mod;
		ans = (ans- tmp +mod)% mod;
	}
	printf("%I64d\n", ans);
	return 0;
}

思路2:l=i, r=n/(n/i) ==>  n/x(l<=x<=r)的值必定是i

跟上面的思路是極像的,只不過這種方法並沒有將n開根號,看懂了上面的,這個應該也懂的差不多了

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
#define ll __int64
ll mod=1e9+7;
int main()
{
	ll n, m;
	scanf("%I64d%I64d", &n, &m);
	ll ans = n%mod*(m%mod)%mod;
	ll sum=0, j, mm;
	for(ll i=1; i<=min(n, m); i++)
	{
	    j = min(n/(n/i), m);
	    ll s1 = i+j, s2 = j-i+1;
	    if(s1%2==0) s1 = (s1/2)%mod, s2%=mod;
	    else s1%=mod, s2 = (s2/2)%mod;
	    mm = s1*s2%mod*(n/i%mod)%mod;
	    sum = (sum+mm)%mod;
	    i = j;
	}
	ans = (ans-sum+mod)%mod;
	printf("%I64d\n", ans);
	return 0;
}