UOJ42. 【清华集训2014】Sum

传送门

Sol

( − 1 ) a = 1 − 2 ( a   m o d   2 ) = 1 − 2 a + 4 ⌊ a 2 ⌋ (-1)^a=1-2(a~mod~2)=1-2a+4\lfloor\frac{a}{2}\rfloor (1)a=12(a mod 2)=12a+42a
那么原式变成 n − 2 ∑ i = 1 n ⌊ d r ⌋ + 4 ∑ i = 1 n ⌊ d r 2 ⌋ n-2\sum_{i=1}^{n}\lfloor d\sqrt{r}\rfloor+4\sum_{i=1}^{n}\lfloor \frac{d\sqrt{r}}{2}\rfloor n2i=1ndr +4i=1n2dr
考虑计算这样一个东西
∑ i = 1 n ⌊ a ∗ r + b c i ⌋ \sum_{i=1}^{n}\lfloor\frac{a*\sqrt{r}+b}{c}i\rfloor i=1ncar +bi
如果 r \sqrt{r} r 是一个整数,直接 Θ ( 1 ) \Theta(1) Θ(1) 计算
否则
k = a ∗ r + b c k=\frac{a*\sqrt{r}+b}{c} k=car +b

如果 k ≥ 1 k\ge 1 k1 那么可以把 k k k 的整数部分的值算出来,变成 0 &lt; k &lt; 1 0&lt;k&lt;1 0<k<1

如果 0 &lt; k &lt; 1 0&lt;k&lt;1 0<k<1,即就是计算 y = k x y=kx y=kx x = n x=n x=n 所围成三角形的整点个数

根据类欧几里得那一套理论,我们用矩形的减去左上角的三角形的

矩形的就是 n ⌊ n k ⌋ n\lfloor nk\rfloor nnk
左上角的三角形的把它关于 y = x y=x y=x 对称,变成求 y = x k y=\frac{x}{k} y=kx x = ⌊ n k ⌋ x=\lfloor nk\rfloor x=nk 所围成三角形的整点个数

这样可以递归下去, n n n 的规模减小得很快,最后计算 n ≤ 1 n\le 1 n1 的答案即可

为了防止精度误差,可以把 k k k 表示成 a ∗ r + b c \frac{a*\sqrt{r}+b}{c} car +b a , b , c a,b,c a,b,c 存下来
每次弄走 g c d gcd gcd 就可以了

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int n, ans, r, test;
double sq;

inline ll Gcd(ll a, ll b) {
	if (!a || !b) return a | b;
	return !b ? a : Gcd(b, a % b);
}

inline ll Solve(ll a, ll b, ll c, ll len) {
	if (!len) return 0;
	register ll sk, nxt, ret, d;
	d = Gcd(Gcd(a, b), c), a /= d, b /= d, c /= d;
	if (len == 1) return (ll)(1.0 * (sq * a + b) / c);
	register double k = 1.0 * (sq * a + b) / c;
	sk = (ll)k, k -= sk, nxt = (ll)(k * len), b -= c * sk;
	ret = len * nxt + sk * (len + 1) * len / 2;
	return ret - Solve(a * c, -b * c, a * a * r - b * b, nxt);
}

int main() {
	scanf("%d", &test);
	while (test--) {
		scanf("%d%d", &n, &r), sq = sqrt(r), ans = sq;
		if (ans * ans == r) printf("%d\n", (ans & 1) ? ((n & 1) ? -1 : 0) : n);
		else printf("%lld\n", n - 2 * Solve(1, 0, 1, n) + 4 * Solve(1, 0, 2, n));
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值