CF1996D Fun

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;
}
posted @ 2024-07-27 11:36  123wwm  阅读(26)  评论(0)    收藏  举报