题解:P5172 Sum
来一篇最没有思维难度的题解。
初步思路
考虑题目要我们求
发现对于奇数
对于偶数
这启发我们利用
现在考虑如何求
考虑类欧可以让我们求出
(
寻找逼近 \sqrt r 的有理数
有一个初步的想法:
这样得到的
考虑这个方法不够精确的原因在哪里,其实在于 sqrt 这个函数本身就无法精确到小数点后
考虑固定分母
__int128_t b=1e15;
__int128_t l=0, r=b*100, a=0;
while(l<=r){
__int128_t mid=(l+r)/2;
//这里的ri就是原题中的r
if(mid*mid<=(__int128_t)ri*b*b){//mid<=sqrt(ri)*b两边开平方,得到mid^2<=ri*b^2
a=mid;
l=mid+1;
}else{
r=mid-1;
}
}
这样可以得到
那么就只能开大分母,并写高精度乘法了:
typedef long long ll;
vector <ll> multiply(vector <ll> a, vector <ll> b){//高精度乘法
vector <ll> res;res.resize(a.size()+b.size()+2);
rep(i, 0, a.size()-1)rep(j, 0, b.size()-1)res[i+j]+=a[i]*b[j];
rep(i, 0, res.size()-1){
if(res[i]>=10){res[i+1]+=res[i]/10;res[i]%=10;}
}
return res;
}
vector <ll> into(__int128_t a){//把a转成高精度形式
vector <ll> res;
while(a){
res.push_back(a%10);
a/=10;
}
return res;
}
bool Greater(vector <ll> a, vector <ll> b){//比较两个高精度形式的数
while(a.back()==0)a.pop_back();
while(b.back()==0)b.pop_back();
if(a.size()!=b.size())return a.size()>b.size();
rrep(i, a.size()-1, 0)if(a[i]!=b[i])return a[i]>b[i];
return false;
}
因为复杂度是
此时有
- 使用 FFT/NTT 代替暴力乘法,复杂度
O(T(\log n)^2\log \log n) 得分未知。 - 优化常数,原先使用 vector 存储十进制下的整数,但是一个 long long 只存一个
[0, 9] 之间的整数太过浪费,于是用 vector 存储十亿进制下的整数,复杂度不变,但是常数得到很大的优化。
AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i, a, b) for(ll i=a;i<=b;i++)
#define rrep(i, a, b) for(ll i=a;i>=b;i--)
__int128_t f(__int128_t n, __int128_t a, __int128_t b, __int128_t c){
__int128_t ans=n*(n+1)/2*((ll)(a/c))+(n+1)*((ll)(b/c));
a%=c;b%=c;
if(a==0)return ans;
__int128_t m=(a*n+b)/c;
ans+=n*m-f(m-1, c, c-b-1, a);
return ans;
}
ll t, n, ri;
#define B 1000000000//B是进制
vector <ll> multiply(vector <ll> a, vector <ll> b){//高精度乘法
vector <ll> res;res.resize(a.size()+b.size()+2);
rep(i, 0, a.size()-1)rep(j, 0, b.size()-1)res[i+j]+=a[i]*b[j];
rep(i, 0, res.size()-1){
if(res[i]>=B){res[i+1]+=res[i]/B;res[i]%=B;}
}
return res;
}
vector <ll> into(__int128_t a){//把a转成十亿进制的高精度形式(每一位存储0~999999999的数)
vector <ll> res;
while(a){
res.push_back(a%B);
a/=B;
}
return res;
}
bool Greater(vector <ll> a, vector <ll> b){//比较两个高精度形式的数
while(a.back()==0)a.pop_back();
while(b.back()==0)b.pop_back();
if(a.size()!=b.size())return a.size()>b.size();
rrep(i, a.size()-1, 0)if(a[i]!=b[i])return a[i]>b[i];
return false;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> t;
while(t--){
cin >> n >> ri;
__int128_t b=1e25;
__int128_t l=0, r=b*100, a=0;
vector <ll> rbb=multiply(multiply(into(ri), into(b)), into(b));//把ri*b*b预计算出来
while(l<=r){//二分
__int128_t mid=(l+r)/2;
if(!Greater(multiply(into(mid),into(mid)),rbb)){
a=mid;
l=mid+1;
}else{
r=mid-1;
}
}
__int128_t cnt1=f(n, a, 0, b)-f(n, a, 0, b*2)*2;//类欧几里得算法
cout << (ll)((n-cnt1)-cnt1) << endl;
}
return 0;
}