给你一个整数N,求满足以下条件的整数对(x,y)的个数对 998244353取模的结果
对于任意一个平方数x2,它与右边的平方数的距离为:
与(x+1)2的距离为(x+1)2−x2=2x+1
与(x+2)2的距离为(x+2)2−x2=4x+4
与(x+3)2的距离为(x+3)2−x2=6x+9
与(x+n)2的距离为(x+n)2−x2=2nx+n2
显然,距离都必须小于等于N
那么对于x,给它最长为N的扩展长度,它能扩展多少个平方数?
设x能扩展的平方数个数为n
(x+n)2−x2≤N
解得: n≤N+x2−x
所以对于每个x,它对答案的贡献是N+x2−x
得到这样的函数:
f(x)=N+x2−x
可以发现,每一个x对答案的贡献可能相同,为了减少枚举次数,可以将对答案贡献相同的x,缩成一个集合,直接加到答案里面。
例如[x,x+t]之间的数,往右扩展N的距离,能碰到的平方数个数都为cnt
那么,答案 +t×cnt 即可.
而这个区间怎么获得?
我们不妨设i,表示每个x对答案的贡献。
可以得到式子:N+x2−x=i
整理得:x=2i(N−i2)
这个x是对答案的贡献为i的区间下限。
由此可以求出,对答案贡献为i−1的区间的上限是 x+1
同理可以求出,对答案贡献为i的区间的上限是 2(i+1)N−(i+1)2+1
也就是当贡献为i+1的时候,区间的下界+1
所以,我们可以设ans[i]为 对答案贡献为i的区间的下限ans[i]
那么对每个贡献i,我们给答案加上:(ans[i]−(ans[i+1]+1)+1)∗i
可以证明的是,i≤N
所以时间复杂度为 O(N)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define PII pair<int,int>
#define INF 1e18
const int mod =998244353;
void slove(){
int n;
cin >> n;
int all = sqrt(n);
vector<int> ans(all + 10);
for (int i = all; i >= 1; i--) {
int x = (n - i * i) / (2 * i);
int cnt = sqrt(n + x * x) - x;
ans[i] = x;
}
ans[0] = ans[1] + 1;
int sum = 0;
for (int i = 1; i <= all - 1; i++) {
sum += (ans[i] - ans[i + 1]) * i % mod;
sum %= mod;
}
//把i = all的情况单独拿出来
sum = (sum % mod + (ans[all] + 1) * all % mod ) %mod;
cout << sum << '\n';
}
signed main(){
slove();
}