UOJ42. 【清華集訓2014】Sum
阿新 • • 發佈:2018-12-09
Sol
\((-1)^a=1-2(a~mod~2)=1-2a+4\lfloor\frac{a}{2}\rfloor\)
那麼原式變成 \(n-2\sum_{i=1}^{n}\lfloor d\sqrt{r}\rfloor+4\sum_{i=1}^{n}\lfloor \frac{d\sqrt{r}}{2}\rfloor\)
考慮計算這樣一個東西
\[\sum_{i=1}^{n}\lfloor\frac{a*\sqrt{r}+b}{c}i\rfloor\]
如果 \(\sqrt{r}\) 是一個整數,直接 \(\Theta(1)\) 計算
否則
設 \(k=\frac{a*\sqrt{r}+b}{c}\)
如果 \(k\ge 1\) 那麼可以把 \(k\) 的整數部分的值算出來,變成 \(0<k<1\)
如果 \(0<k<1\),即就是計算 \(y=kx\) 與 \(x=n\) 所圍成三角形的整點個數
根據類歐幾里得那一套理論,我們用矩形的減去左上角的三角形的
矩形的就是 \(n\lfloor nk\rfloor\)
左上角的三角形的把它關於 \(y=x\) 對稱,變成求 \(y=\frac{x}{k}\) 與 \(x=\lfloor nk\rfloor\) 所圍成三角形的整點個數
這樣可以遞迴下去,\(n\) 的規模減小得很快,最後計算 \(n\le 1\) 的答案即可
為了防止精度誤差,可以把 \(k\) 表示成 \(\frac{a*\sqrt{r}+b}{c}\) 將 \(a,b,c\) 存下來
每次弄走 \(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; }