BZOJ 3817 Sum

Description

给定正整数N,R。求

Input

第一行一个数 T,表示有 T 组测试数据。
接下来 T 行,每行两个正整数 n,r。

Output

输出 T 行,每行一个整数表示答案。

Sample Input

3
3 5
3 6
3 7

Sample Output

3
1
-1

HINT

对于 100% 的数据,满足 n≤10^9,r≤10^4,T≤10^4。
关于类欧几里得的介绍:ZYYS
设$x=\sqrt r$,则
$$
\begin{align}
-1^{dx } & =1-2( dx \% 2) \\
&=1-2(dx - \frac{dx}{2} * 2) \\
&= 1+4\frac{dx}{2} + 2 dx
\end{align}
$$
那么
$$原式 =n + 4 \sum_{d=1}^{n} \frac{dx}{2}-2\sum_{d=1}^{n}dx$$
但系数是一个实数,写作$ans=\sum_{i=1}^{n}\lfloor k*i \rfloor$
$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 \rfloor}[k*i>j]$
按照类欧的套路,移项
$ans=\sum_{i=1}^{n}\sum_{j=1}^{\lfloor k*n \rfloor}[i>\lfloor \frac{j}{k} \rfloor]$
交换枚举顺序
$ans=\sum_{j=1}^{\lfloor k*n \rfloor}n-\lfloor \frac{j}{k} \rfloor$
$ans=\lfloor k*n \rfloor*n-\sum_{j=1}^{\lfloor k*n \rfloor}\lfloor \frac{j}{k} \rfloor$
把$\frac{1}{k}$的分母有理化,发现后面这部分可以递归
我们发现在$k<1$递归$\frac{1}{k}$时,下一个$k$会大于1,这样下一个$n$会变大
我们可以用这个方法;
$ans=\sum_{i=1}^{n}\lfloor k*i \rfloor$
$ans=\sum_{i=1}^{n}\lfloor k*i-\lfloor k \rfloor*i+\lfloor k \rfloor*i \rfloor$
$ans=\sum_{i=1}^{n}\lfloor k*i-\lfloor k \rfloor*i \rfloor+\lfloor k \rfloor*i$
$ans=\lfloor k \rfloor*\frac{n*(n+1)}{2}+\sum_{i=1}^{n}\lfloor k*i-\lfloor k \rfloor*i \rfloor$
$\lfloor k*i-\lfloor k \rfloor*i \rfloor=\lfloor \frac{a*x+b-c*\lfloor \frac{a*x+b}{c} \rfloor}{c} *i\rfloor$
把当前的$k$替换
$k=\frac{a*x+b-c*\lfloor \frac{a*x+b}{c} \rfloor}{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",n);
40         }
41       else
42         {
43           if (n%2==0)
44         printf("0\n");
45           else printf("-1\n");
46         }
47     }
48       else
49     {
50       ans=n+(cal(1,0,2,n)<<2)-(cal(1,0,1,n)<<1);
51       printf("%lld\n",ans);
52     }
53     }
54 }

 

posted @ 2018-02-09 15:55  Z-Y-Y-S  阅读(377)  评论(0编辑  收藏  举报