【BZOJ3817(Sum(类欧几里得))】传送门
题意:
给定正整数 n,r ,求:
∑d=1n(?1)?dr√?
题解:
有点像类欧几里得。
只需要知道:
∑d=1n?dr√?%2
又因为
?x?%2=?x???x2??2
那么问题转化为求
∑d=1n?dx?
显然这是一条从原点出发的直线,要求的是它的下半部分的整数点。
有一个结论是如果这条直线的斜率大于1,那么先减掉1的斜率并加上这个斜率带来的一个直角三角形的贡献,这个新得到的直线覆盖的整点即为原来直线还没有加上的点。
类似于欧几里得,现在我们得到了一个斜率小于1的直线,那么考虑翻转这个坐标系来枚举y轴,发现又变成了一个子问题,这样迭代只会进行O(logn) 次。
具体的实现由于精度问题,我写的 long double 类并不能通过,所以要用 long long 类做分子分母来表示,记现在直线的斜率为 ax+bc(<1) ,要延伸到长度为 n 的位置。
先计算出在 n 的时候直线可以覆盖的点数 t ,并把 n?t 加入贡献中,此时我们多加入了上半部分的三角形,再把这个直线翻转,长度变为 t ,斜率变为 cax+b=c(ax?b)a2r?b2 ,减去这部分的贡献即可,这部分可以递归实现,代码非常的短。
Fastio部分可以忽略掉
#include
typedef long long ll;
typedef long double db;
using namespace std;
const int R_LEN=(1<<18)|1;
struct Fast_io{
char ibuf[R_LEN],obuf[R_LEN],*s,*t,*wt;
int buf[50];
Fast_io(){s=ibuf,t=ibuf;
wt=obuf;
}
~Fast_io(){fwrite(obuf,1,wt-obuf,stdout);
}
inline void print(char c){
(wt==(obuf+R_LEN)) && (fwrite(obuf,1,R_LEN,stdout),wt=obuf);
*wt++=c;
}
template
inline void W(T x){
if(x<0){print('-');
x=-x;
}
if(!x){print('0');
return;
}
while(x){buf[++buf[0]]=x%10;
x/=10;
}
while(buf[0])print(buf[buf[0]--]+'0');
}
inline char getc(){
(s==t) && (t=(s=ibuf)+fread(ibuf,1,R_LEN,stdin));
return (s==t)?-1:*s++;
}
inline int rd(){
char ch=getc();
int i=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;
ch=getc();
}
while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';
ch=getc();
}
return i*f;
}
}io;
int T,n,r;
db R,mxlen;
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;
}
inline ll solve(ll a,ll b,ll c,ll len){
if(!len) return 0;
ll t=gcd(a,gcd(b,c));
a/=t;
b/=t;
c/=t;
t=(a*R+b)/c;
ll sum=(len*(len+1)>>1)*t;
b-=c*t;
t=(a*R+b)*len/c;
return sum+t*len-solve(a*c,-b*c,a*a*r-b*b,t);
}
int main(){
for(T=io.rd();
T;
T--){
n=io.rd(),r=io.rd();
R=sqrt(r);
int t=(int)(R+0.5);
if(t*t==r){io.W((t&1)?((n&1)?-1:0):n);
io.print('\n');
continue;
}
int odd_cnt=solve(1,0,1,n);
odd_cnt-=2*solve(1,0,2,n);
io.W(n-2*odd_cnt),io.print('\n');
}
}