【清华集训2014】Sum

题目链接

题目描述

求:
∑ d = 1 n ( − 1 ) ⌊ d ∗ r ∗ d ⌋ \sum_{d=1}^{n} (-1)^{\big\lfloor \sqrt{d*r*d} \big \rfloor} d=1n(1)drd

Sol

就是:
∑ d = 1 n ( − 1 ) ⌊ d r ⌋ \sum_{d=1}^{n} (-1)^{\big\lfloor d\sqrt{r} \big \rfloor} d=1n(1)dr

如果 r r r是完全平方数直接算,反之:

显然我们只需要知道有多少个 ⌊ d r ⌋ \big\lfloor d\sqrt{r} \big \rfloor dr 为奇数或偶数,那么想到这样化个式子:
∑ d = 1 n ( 1 − 2 ∗ ( ⌊ d r ⌋   m o d   2 ) \sum_{d=1}^n (1-2*\big (\lfloor d\sqrt r \rfloor \ mod\ 2 \big ) d=1n(12(dr  mod 2)
然后用除法拆掉 mod,化简,记 x = r x=\sqrt r x=r

n − 2 ∗ ∑ d = 1 n ( ⌊ d x ⌋ − ⌊ d x 2 ⌋ ∗ 2 ) n-2*\sum_{d=1}^n \big (\lfloor dx \rfloor-\lfloor \frac{dx}{2} \rfloor*2 \big) n2d=1n(dx2dx2)

于是只需要会求解:
∑ i = 1 n ⌊ A C ∗ i ⌋ \sum_{i=1}^{n} \bigg \lfloor \frac{A}{C}*i \bigg \rfloor i=1nCAi
但是由于这里 A A A是一个实数,直接做会有精度误差,为了类欧的需要,所以可以用带上 x x x的项来表示:
∑ i = 1 n ⌊ A x + B C ∗ i ⌋ \sum_{i=1}^{n} \bigg \lfloor \frac{Ax+B}{C}*i \bigg \rfloor i=1nCAx+Bi

这样就是个比较裸的类欧了,用矩形中的整点个数减去上三角的整点个数,由于 A A A的后面有个 x x x,我们不能够直接把 A A A 模掉 C C C,所以可以直接把多的 A A A 减在 B B B上,这样问题的规模就可以缩小了,因为 n n n 会不断变小

m = ⌊ A x + B C ∗ n ⌋ m=\lfloor \dfrac{Ax+B}{C}*n \rfloor m=CAx+Bn,那么把该直线关于 y = x y=x y=x对称( 斜 率 之 积 为 1 斜率之积为1 1),分母有理化后,可得以下递推式:
F ( n , A , B , C ) = n ∗ m − F ( m , A ∗ C , − C ∗ B , A ∗ A ∗ r − B ∗ B ) F(n,A,B,C)=n*m-F(m,A*C,-C*B,A*A*r-B*B) F(n,A,B,C)=nmF(m,AC,CB,AArBB)
其中 F ( n , A , B , C ) F(n,A,B,C) F(n,A,B,C)表示参数为这些值时的答案

#include
using namespace std;
typedef long double ldb;
typedef long long ll;
template<class T>inline void init(T&x){
	x=0;char ch=getchar();bool t=0;
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
	if(t) x=-x;return;
}

ldb R;
ll n,r;
ll gcd(ll a,ll b){return b? gcd(b,a%b):a;}
ll likegcd(ll n,ll A,ll B,ll C){
	if(!n) return 0;
	int d=gcd(A,gcd(B,C));A/=d,B/=d,C/=d;//除掉 gcd 可以防止爆 long long
	ldb K=((ldb)A*R+(ldb)B)/(ldb)C;ll k=(ll)(K);
	B-=k*C;//是把多的 A 减在 B 上面 !
	K=K-1.00*k;ll m=(ll)(K*n);
	ll ret=n*(n+1)/2*k;
	return ret+n*m-likegcd(m,A*C,-C*B,A*A*r-B*B);
}
int main()
{
	int T;init(T);
	while(T--){
		init(n);init(r);R=sqrt((ldb)r);ll g=(ll)(R);
		if(g*g==r) {if(r&1) printf("%d\n",(n&1)? (-1):0);else printf("%d\n",n);}
		else {
			ll ans=1ll*n-((likegcd(n,1,0,1)-(likegcd(n,1,0,2)<<1))<<1);
			printf("%lld\n",ans);
		}
	}
}

你可能感兴趣的:(======数论======,======题解======,类欧几里得算法)