珂朵莉 - log - 快速幂 - 乘2的逆 - 费马小定理

在太阳西斜的这个世界里

f(a,b,k)=\sum_{i=1}^{\infty} (a*i+b*\lfloor log_2(i) \rfloor)*[a*i+b*\lfloor log_2(i) \rfloor\leq k]

令 f(a,b,k)=∑​(a∗i+b∗⌊log2​(i)⌋)∗[a∗i+b∗⌊log2​(i)⌋≤k]

给定a,b,k.  求 f(a,b,k) mod 1000000007 的值。

注意:

  • 1000000007 为质数。
  • 对于表达式[condition] ,当 condition 为真时表达式的值为 1 ,否则为 0 .

    输入格式

    三个整数分别代表 a,b,k 。

    数据范围:

  • 1≤a,b≤10^9
  • 1≤k≤10^18

    输入输出样例

  • 输入:2 2 15
  • 输出:42
  • 输入:2 2 50
  • 输出:578

 答:枚举i的话会比较难,每一个i都要算log,这时候不难发现,一个log(i)对应多个i值,我们可以枚举log(i)是多少:0--62;最大就是62,很好模拟

通过log的值,我们可以得出在当前log值的 i 的左右边界:l = (1<< i), r = (1 << i) - 1 + (1 << i);

注意,r的最大值是(k - i * b) / a。

另外,还有注意,我们可以简单看出ans=a * (1 + ansi)* ansi) / 2 + b * sum_the_log(ansi)

但是,x/2%mod的值和x%mod/2的值不一定相等,但我们如果在/2之前不%的话,long long 很容易会爆,这时候我们要:不能直接除2,要乘2的逆//某数学定理-费马小定理

为什么要逆元
当一个题目让你求方案数时常要取余,虽然(a+b)%p=(a%p+b%p)%p(a-b)%p=(a%p-b%p)%p(a* b)%p=(a%p*b%p)%p

但是:(\frac{a}{b})%p\neq (\frac{a%p}{b%p})%p
由于会出现这种情况,所以就要逆元了
一般情况下,当a*x=1时,x 是 a 的倒数,x=1/a
毕竟是取余,所以当ax≡1(mod p)时, x 叫做 a 关于 p 的逆元,用 a^{-1}表示
于是(\frac{a}{b})%p=(a%p*b^{-1}%p)%p
这样就把除法转换成乘法,解决了问题

如何求逆元
前提:gcd(a,p)=1,快速幂,inv = ksm(2, mod - 2, mod);

a 的逆=a的(mod-2)次方

关于乘2的逆的资料:欧拉定理 & 费马小定理 - OI Wiki

!!全程多多%mod!! 注意开longlong

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 1e9 + 7;
ll a,b,k;
ll ans=0;
ll ksm(ll x, ll y, ll o)
{
	if (x == 0 && y == 0) return 1;
	if (x == 0) return 0;
	ll ret = 1;
	while (y)
	{
		if (y & 1) ret = (ret * x) % o;
		x = (x * x) % o;
		y >>= 1;
	}
	return (ret % o);
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> a >> b >> k;
	ll inv = ksm(2, mod - 2, mod); //快速幂
	//不能直接除2,要乘2的逆
	for (ll i = 0; i <= 62; i++)
	{
		ll l = (1ll << i), r = (1ll << i) - 1 + (1ll << i);
		//1ll表示longlong类型的1 
		r = min(r, (k - i * b) / a);
		//r的最大值就是 (k - i * b) / a,超过了就不可能啦 
		//接下来找i的最大值
		if (r >= l)
		{
			(ans += (r - l + 1) % mod * b % mod * i % mod) %= mod;
			(ans += (l + r) % mod * ((r - l + 1) % mod) % mod * inv % mod * a % mod) %= mod;
			//ans += (r - l + 1) * b * i;
			//ans += (l + r) * (r - l + 1) / 2 * a;
		}
		else break; 
	}
	cout << (ans % mod + mod) % mod << endl;
	//这题不会负数,but最好写上习惯避免负数
	return 0;
}
//枚举log(i)是多少:0--62;
//得出范围l,r
//乘法会爆则用除法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值