题意:给你n,r,求sigma_i=[1,n] (-1)^[i*r^0.5].
n<=1e9.
标程:
1 #include<cstdio> 2 #include<cmath> 3 using namespace std; 4 int read() 5 { 6 int x=0,f=1;char ch=getchar(); 7 while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} 8 while (ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); 9 return x*f; 10 } 11 double x; 12 int n,r; 13 int gcd(int x,int y) {return (!y)?x:gcd(y,x%y);} 14 int solve(int n,int a,int b,int c)//sigma_i=[1,n] ([(bx+c)/a*i]) 15 { 16 if (!n) return 0; 17 int k=gcd(a,gcd(b,c));a/=k,b/=k,c/=k;//最简分式,避免爆int 18 k=(b*x+c)/a; int sum=n*(n+1)/2*k; c-=k*a;//去掉整数部分,使得k<1 19 k=(b*x+c)/a*n;//纵坐标范围 20 return sum+n*k-solve(k,b*b*r-c*c,a*b,-a*c);//下一层递归,分母有理化 21 } 22 int main() 23 { 24 int T=read(); 25 while (T--) 26 { 27 n=read();r=read();x=sqrt(r); 28 if ((int)x==x) printf("%d ",(r&1)?((n&1)?-1:0):n);//直接算x为有理数的情况 29 else printf("%d ",n+4*solve(n,2,1,0)-2*solve(n,1,1,0)); 30 } 31 return 0; 32 }
题解:类欧几里得
solution1:设x=r^0.5.那么x>2时,x-=2奇偶性不变。如果1<x<2,x=2-x,奇偶性不变。所以只用考虑x<1时的情况。因为x*1/x=1,所以i每增加1/x,会改变一次(-1)的正负状态。以1/x为单位长度对1~n的序列轮流黑白染色,整点上黑色-白色的数量即为最后的答案。每一段至少会有[1/x]个整点,不妨把每一段中[1/x]的整数区间抵消(最后一块如果为黑就暴力加贡献,还有多余的部分),然后对{1/x}的单位长度,大致是{1/x}*n/([1/x])大小的区间继续递归。时间复杂度logn。
solution2:
一般情况的类欧算法用于解决求sigma_i=[1,n]([(bx+c)/a*i])的问题,a,b,c都是自定义的常数,x必须是无理数。
设k=(bx+c)/a。那么就相当于求sigma([ki])。也就是横坐标从1到n,y=ki的直线下方、x轴上方包含多少个整数点。k>1,可以对整数部分直接统计。考虑k<1。对于每一条y=j(j<=[kn]),这一条直线上包含在给定区间上的n-[j/k]个点(根据相似,一共有[(kn-j)/kn*n]+1=[n-j/k]+1=n-[j/k]个(k为无理数,不考虑边界))。子问题:求sigma_j=[1,[k/n]] ([j/k])。把1/k的分母有理化一下,就可以递归求解。时间复杂度log^2(n),递归+gcd约分。