CF1996D Fun
有一个很奇葩的做法,跟官方做法完全不一样,一定要来分享一下。
由 \(ab+ac+bc\le n\) 我们可以得出 \(a(b+c)+bc\le n\)。所以我们考虑枚举 \(a\) 和 \(b,c\) 的和 \(k\),由于有 \(ak\le n\) 的限制,所以这样枚举的复杂度为 \(\lfloor\frac{n}{1}\rfloor+\lfloor\frac{n}{2}\rfloor+\lfloor\frac{n}{3}\rfloor+\dots=O(n\log n)\)。
接下来我们考虑数出满足这样条件的 \(b,c\)。问题转化为两个数的和为定值,求两个数的乘积 \(\le n-ak\)。我们设一个数为 \(x\),注意 \(x\) 的定义域为 \([1,k-1]\)。则 \(x(k-x)\le n-ak\),化简为 \(x^2-kx+n-ak\ge 0\),\(\Delta\) 为 \(k^2-4(n-ak)\)。若 \(\Delta\le0\),则上式是恒成立的,答案直接加上 \(k-1\) 即可。若 \(\Delta>0\),我们能够求出两个根 \(x_1,x_2\),那么答案为 \([1,x_1]\) 和 \([x_2,k-1]\) 中的整数个数。
代码:
#include <bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define pb push_back
#define mk make_pair
#define ll long long
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef vector <int> vi;
typedef pair <int, int> pii;
inline int rd() { int x = 0, f = 1; char c = getchar(); while (!isdigit(c)) f = c == '-' ? -1 : f, c = getchar(); while (isdigit(c)) x = (x<<3)+(x<<1)+(c^48), c = getchar(); return x*f; }
template <typename T> inline void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x/10); putchar(x%10+48); }
void solve() {
int n, x, ans = 0; cin >> n >> x;
for (int a = 1; a <= x; ++a)
for (int k = 2; a+k <= x && a*k <= n-1; ++k) {
int r = n-a*k, d = k*k-4*r;
if (d <= 0) ans += k-1;
else {
double x1 = (k-sqrt(d))/2, x2 = (k+sqrt(d))/2;
ans += max((int)floor(x1), 0ll)+max((int)(k-ceil(x2)), 0ll);
}
}
cout << ans << '\n';
}
signed main() {
int t = rd();
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号