Description
给定正整数N,R。求
Input
第一行一个数 T,表示有 T 组测试数据。
接下来 T 行,每行两个正整数 n,r。
Output
输出 T 行,每行一个整数表示答案。
Sample Input
3
3 5
3 6
3 7
3 5
3 6
3 7
Sample Output
3
1
-1
1
-1
HINT
对于 100% 的数据,满足 n≤10^9,r≤10^4,T≤10^4。
关于类欧几里得的介绍:ZYYS
设$x=sqrt r$,则
$$
egin{align}
-1^{dx } & =1-2( dx \% 2) \
&=1-2(dx - frac{dx}{2} * 2) \
&= 1+4frac{dx}{2} + 2 dx
end{align}
$$
那么
$$原式 =n + 4 sum_{d=1}^{n} frac{dx}{2}-2sum_{d=1}^{n}dx$$
$$
egin{align}
-1^{dx } & =1-2( dx \% 2) \
&=1-2(dx - frac{dx}{2} * 2) \
&= 1+4frac{dx}{2} + 2 dx
end{align}
$$
那么
$$原式 =n + 4 sum_{d=1}^{n} frac{dx}{2}-2sum_{d=1}^{n}dx$$
但系数是一个实数,写作$ans=sum_{i=1}^{n}lfloor k*i
floor$
$k=frac{a*x+b}{c}$ 这里x等于根号r
类欧的套路,将其转化为函数含义,也就是:
函数$y=k*x$与x轴,与$x=1$和$x=n$围成的梯形有多少整点
为了方便,这里不考虑函数线上的整点,而在开始特判(显然有整点代表x为整数)
如果$k<1$
那么有
$ans=sum_{i=1}^{n}sum_{j=1}^{lfloor k*n floor}[k*i>j]$
按照类欧的套路,移项
$ans=sum_{i=1}^{n}sum_{j=1}^{lfloor k*n floor}[i>lfloor frac{j}{k} floor]$
交换枚举顺序
$ans=sum_{j=1}^{lfloor k*n floor}n-lfloor frac{j}{k} floor$
$ans=lfloor k*n floor*n-sum_{j=1}^{lfloor k*n floor}lfloor frac{j}{k} floor$
把$frac{1}{k}$的分母有理化,发现后面这部分可以递归
我们发现在$k<1$递归$frac{1}{k}$时,下一个$k$会大于1,这样下一个$n$会变大
我们可以用这个方法;
$ans=sum_{i=1}^{n}lfloor k*i floor$
$ans=sum_{i=1}^{n}lfloor k*i-lfloor k floor*i+lfloor k floor*i floor$
$ans=sum_{i=1}^{n}lfloor k*i-lfloor k floor*i floor+lfloor k floor*i$
$ans=lfloor k floor*frac{n*(n+1)}{2}+sum_{i=1}^{n}lfloor k*i-lfloor k floor*i floor$
$lfloor k*i-lfloor k floor*i floor=lfloor frac{a*x+b-c*lfloor frac{a*x+b}{c} floor}{c} *i floor$
把当前的$k$替换
$k=frac{a*x+b-c*lfloor frac{a*x+b}{c} floor}{c}$
这样$k$就小于1了
然后按$k<1$的情况递归
由于每次$n$都会乘以一个小于1的数,所以复杂度大概是$O(logn)$
不过为了防止暴longlong要提出gcd,用辗转相除
最后复杂度是$O(Tlog^{2}n)$
那么有
$ans=sum_{i=1}^{n}sum_{j=1}^{lfloor k*n floor}[k*i>j]$
按照类欧的套路,移项
$ans=sum_{i=1}^{n}sum_{j=1}^{lfloor k*n floor}[i>lfloor frac{j}{k} floor]$
交换枚举顺序
$ans=sum_{j=1}^{lfloor k*n floor}n-lfloor frac{j}{k} floor$
$ans=lfloor k*n floor*n-sum_{j=1}^{lfloor k*n floor}lfloor frac{j}{k} floor$
把$frac{1}{k}$的分母有理化,发现后面这部分可以递归
我们发现在$k<1$递归$frac{1}{k}$时,下一个$k$会大于1,这样下一个$n$会变大
我们可以用这个方法;
$ans=sum_{i=1}^{n}lfloor k*i floor$
$ans=sum_{i=1}^{n}lfloor k*i-lfloor k floor*i+lfloor k floor*i floor$
$ans=sum_{i=1}^{n}lfloor k*i-lfloor k floor*i floor+lfloor k floor*i$
$ans=lfloor k floor*frac{n*(n+1)}{2}+sum_{i=1}^{n}lfloor k*i-lfloor k floor*i floor$
$lfloor k*i-lfloor k floor*i floor=lfloor frac{a*x+b-c*lfloor frac{a*x+b}{c} floor}{c} *i floor$
把当前的$k$替换
$k=frac{a*x+b-c*lfloor frac{a*x+b}{c} floor}{c}$
这样$k$就小于1了
然后按$k<1$的情况递归
由于每次$n$都会乘以一个小于1的数,所以复杂度大概是$O(logn)$
不过为了防止暴longlong要提出gcd,用辗转相除
最后复杂度是$O(Tlog^{2}n)$
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 typedef long long lol; 8 double t; 9 lol r; 10 lol gcd(lol a,lol b) 11 { 12 if (!b) return a; 13 return gcd(b,a%b); 14 } 15 lol cal(lol a,lol b,lol c,lol n) 16 { 17 if (n==0) return 0; 18 lol g=gcd(a,gcd(b,c)); 19 a/=g;b/=g;c/=g; 20 lol k=(t*a+b)/c; 21 lol ans=n*(n+1)/2*k; 22 b-=k*c; 23 k=(t*a+b)/c*n; 24 ans+=k*n-cal(a*c,-b*c,a*a*r-b*b,k); 25 return ans; 26 } 27 int main() 28 {int T; 29 lol n,ans; 30 cin>>T; 31 while (T--) 32 { 33 scanf("%lld%lld",&n,&r); 34 t=sqrt((double)r); 35 if ((lol)t==t) 36 { 37 if ((lol)t%2==0) 38 { 39 printf("%lld ",n); 40 } 41 else 42 { 43 if (n%2==0) 44 printf("0 "); 45 else printf("-1 "); 46 } 47 } 48 else 49 { 50 ans=n+(cal(1,0,2,n)<<2)-(cal(1,0,1,n)<<1); 51 printf("%lld ",ans); 52 } 53 } 54 }