在太阳西斜的这个世界里
令 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*x=1时,x 是 a 的倒数,x=1/a
毕竟是取余,所以当ax≡1(mod p)时, x 叫做 a 关于 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
//乘法会爆则用除法