[ZZOJ#31]类欧几里得
试题描述
这是一道模板题。
给出 (a, b, c, n),请你求出 (sum_{x=0}^n{lfloor frac{a cdot x + b}{c} floor})
输入
一行四个正整数 (a, b, c, n)。
输出
一个整数表示答案。
输入示例1
10 7 3 3
输出示例1
28
输入示例2
36976101 240442820 735275034 66441189
输出示例2
110998229606855
数据规模及约定
对于 (50\%) 的数据,有 (n le 10^7)
对于 (100\%) 的数据,保证 (a, b, c, n le 10^9),答案不会超过 (9223372036854775807)(int64 最大值)。
题解
以前出出来的,发现忘记写博客了,来补个坑。
类欧模板。讲解随便就能百度到。
主要思路就是数形结合,将此题转化成“求直线下方整点个数”。对于 (c ge a) 或 (b ge a) 的情况,将整数部分 (lfloor frac{c}{a} floor) 和 (lfloor frac{b}{a} floor) 先算出来,再考虑补上没记上的部分,于是将问题变成了 (b, c < a) 的情况。对于这个情况,就是求一个直角梯形内部整点个数(这个直角梯形 (y) 轴上结局和斜率都小于 (1) 的性质保证后面的子问题规模会缩小),我们考虑不按 (x) 坐标枚举,变成按 (y) 坐标枚举,推一推式子发现能转化成子问题。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)
int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
#define LL long long
LL solve(LL a, LL b, LL c, LL n) {
if(!n) return b / c;
if(n < 0) return 0;
if(a >= c || b >= c) return b / c * (n + 1) + a / c * n * (n + 1) / 2 + solve(a % c, b % c, c, n);
LL m = (a * n + b) / c;
return n * m + m - solve(c, a - b + c - 1, a, m - 1);
}
int main() {
int a = read(), b = read(), c = read(), n = read();
printf("%lld
", solve(a, b, c, n));
return 0;
}