1. 程式人生 > >BZOJ3817 Sum(類歐幾裏得算法)

BZOJ3817 Sum(類歐幾裏得算法)

b+ pan 倒數 小數 -- 斜率 logs 如何 線下

設$t=\sqrt r$,原題轉化為$\sum_{x=1}^n(4*\lfloor\frac{tx}2\rfloor-2*\lfloor tx\rfloor)$
考慮如何求$\sum_{x=1}^n\lfloor\frac{bt+c}ax\rfloor$
開始我寫了一個真歐幾裏得來求直線下整點數目,然後由於裏頭含小數所以不對。
於是學習了一下新姿勢,思想其實差不多。
先把a,b,c同時除以gcd(a,b,c),防止爆int。
之後把斜率變成$\frac{bt+c}a-\lfloor\frac{bt+c}a\rfloor$,並計算對應貢獻。
第三步把x,y軸互換,這時斜率變成了倒數,即$\frac a{bt+c}=\frac {abt-ac}{b^2t^2-c^2}$
特判r是完全平方數的時刻,因為這樣直線上會有點,所以減的時候會減多。
補充:真歐幾裏得算法:
$$\sum_{0<=x<n} \lfloor \frac{ax+b}{c} \rfloor=n*\lfloor \frac{b}{c} \rfloor+\frac{n*(n-1)}{2}*\lfloor \frac{a}{c} \rfloor+\sum_{0<=x<\lfloor \frac{(a\%c)*n+b\%c}{c} \rfloor} \lfloor \frac{cx+(an+b)\%c}{a\%c} \rfloor$$

#include <cstdio>
#include <cmath>
 
int T,n,r;
double t;
int gcd(int a,int b) {return b?gcd(b,a%b):a;}
int sol(int n,int a,int b,int c) {
    if (!n) return 0;
    int tmp=gcd(gcd(a,b),c); a/=tmp; b/=tmp; c/=tmp;
    tmp=(t*b+c)/a; int sum=1ll*n*(n+1)*tmp>>1;
    c-=tmp*a; tmp=(t*b+c)*n/a;
    
return sum+n*tmp-sol(tmp,b*b*r-c*c,a*b,-a*c); } int main() { scanf("%d",&T); while(T--) { scanf("%d%d",&n,&r),t=sqrt(r); if((int)t==t) printf("%d\n",(r&1)?((n&1)?-1:0):n); else printf("%d\n",n+4*sol(n,2,1,0)-2*sol(n,1,1,0)); } return 0; }

BZOJ3817 Sum(類歐幾裏得算法)