bzoj3817 Sum 类欧几里得算法

这道题目solution写了两种做法,都讲一下吧。

首先,令x=r^0.5,显然,如果x>2,则可以不断减2到小于二;如果x>1,那么变为2-x。因此此时必有x<1。(特判r为完全平方数的情况)。那么令y=1/x,则:

题目等价于在数轴从0~n,以y长度为一个区间(左闭右开)黑白交替染色,求黑色部分覆盖的整点减去白色部分覆盖的整点。然后把最后面零散的部分暴力计算,如果最后一个是黑色的也暴力计算。那么这个时候黑白段数相等,且每一段黑(白)都至少覆盖[y]个点恰好各自抵消[y],那么将每一段都去掉[y]的长度,则n’={y}/y*n,y‘={y}继续计算。显然y>1则y>1+{y}>2{y},因此为logN。

另一种为类欧几里得算法。感觉这一种比较有用就用了这一种,如下:

欧几里得算法:计算gcd(x,y),可以由gcd(y,x%y)得到。类欧几里得算法同理。

令x=r^0.5, 显然,(-1)^[d*x]为=1+2*(2[d*x/2]-[d*x]),那么相当于求:

n+4*Σ(d=1,n)[d*x/2]-2*Σ(d=1,n)[d*x],来看一般形式即求Σ(i=1,n) [(bx+c)/a*i]

如果[(bx+c)/a]>=1,那么显然可以把整数部分先单独求出来;那么不妨令k=(bx+c)/a<1,则题目相当于求线段y=kx(0<x<=n)与x正半轴之间区域的整点的个数(包括线段上),如图:

上图中就是A~G,B~F,C~E和D(还有D左边那个QAQ作图的时候没注意)。注意原本我们是枚举横坐标i然后下取整,但是当k<1的时候显然纵坐标的范围小于横坐标,因此我们枚举纵坐标j,那么有j<=[kn],然后由相似得到直线y=j上的整点个数为[(kn-j)/k]+1=n-[j/k],那么实际上就是求出Σ(j=1,[kn]) [j/k],可以递归处理。这样就是O(logN)的。为了避免爆int(long long),需要每次递归的时候约分,因此为O(log^2N),另外1/k需要分母有理化。可以发现这就是一个直角三角形不断压缩翻转的过程。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;

int n,m; double t;
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
int gcd(int x,int y){ return (y)?gcd(y,x%y):x; }
int solve(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)/a*n;
	return sum+n*tmp-solve(tmp,b*b*m-c*c,a*b,-a*c);
}
int main(){
	int cas=read();
	while (cas--){
		n=read(); m=read(); t=sqrt(m);
		if ((int)t==t) printf("%d\n",(m&1)?((n&1)?-1:0):n);
		else printf("%d\n",n+(((solve(n,2,1,0)<<1)-solve(n,1,1,0))<<1));
	}
	return 0;
}

by lych

2016.5.8

时间: 2024-10-10 00:02:26

bzoj3817 Sum 类欧几里得算法的相关文章

[P5170] 类欧几里得算法

のすたの"类欧几里得算法"第二题 P5170 [题意]已知\(n,a,b,c\),求 \[ \begin{aligned} f_{1}(a,b,c,n)&=\sum_{i=0}^n\lfloor\dfrac{ai+b}{c}\rfloor\f_{2}(a,b,c,n)&=\sum_{i=0}^n\lfloor\dfrac{ai+b}{c}\rfloor^2\f_{3}(a,b,c,n)&=\sum_{i=0}^n\lfloor\dfrac{ai+b}{c}\rf

BZOJ3817 Sum(类欧几里得算法)

设$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

类欧几里得算法浅谈(部分)

学习类欧几里得算法,因为是蒟蒻,感觉网上很多都看不懂,所以自己写一篇快活快活 第一类求和式: \(F(a,b,c,n)=\sum_{i=0}^n\lfloor\frac{a*i+b}{c}\rfloor\) 对于这样形式的求和,我们有以下的推导: 1.当\(a>=c\)并且\(b>=c\)时,我们有: 对于\(\lfloor\frac{a}{c}\rfloor\), 它实际等价于\(\lfloor\frac{a\mod c}{c}\rfloor+\lfloor\frac{a}{c}\rfloo

类欧几里得算法

对于求和式 $f(a,b,c,n)=\sum_{i=0}^n \lfloor \frac{ai+b}{c} \rfloor$ 当 $a \geq c$ 或 $b \geq c$ 时,设 $a'=a \; mod \; c$,$b'=b \; mod \; c$,有 $$\begin{align*} f(a,b,c,n) = & \sum_{i=0}^n \; \lfloor \frac{ai+b}{c} \rfloor \\ = & \sum_{i=0}^n \; \lfloor \fra

数论,类欧几里得算法

类欧几里得部分转载自不来也不去的一只失忆蝴蝶.%%%

模板 - 类欧几里得算法

用来快速求解 $\sum\limits_{i=0}^{n}\lfloor \frac{ai+b}{c} \rfloor,\sum\limits_{i=0}^{n}{\lfloor \frac{ai+b}{c} \rfloor}^2,\sum\limits_{i=0}^{n}i\lfloor \frac{ai+b}{c} \rfloor $ 有多快呢?据说是log的?反正abc取1e9可以200ms过1e5组询问-- #include <bits/stdc++.h> typedef long l

[补档计划] 类欧几里得算法

$$\begin{aligned} f(a, b, c, n) & = \sum_{i = 0}^n \lfloor \frac{ai + b}{c} \rfloor \\ & = \sum_{i = 0}^n \sum_{j = 0}^{m-1} [j < \lfloor \frac{ai + b}{c} \rfloor] \\ & = \sum_{i = 0}^n \sum_{j = 0}^{m-1} [j + 1 \le \lfloor \frac{ai + b}{c}

【LuoguP4433】[COCI2009-2010#1] ALADIN(含类欧几里得算法推导)

题目链接 题意简述 区间赋值模意义下等差数列,询问区间和 \(N\leq 10^9,Q\leq 10^5\) Sol 每次操作就是把操作区间\([L,R]\)中的数赋值成: \[(X-L+1)*A\ mod\ B\] 考虑用线段树维护. 我们只需要能快速知道一段区间\([l,r]\)被覆盖后的和就行了,因为覆盖的标记易于下传: \[\sum_{i=l}^{r} (i-L+1)*A\ mod\ B\] 根据基础的数学知识,mod显然不好算,把它拆开: \[\sum_{i=l}^r (i-L+1)*

[BZOJ3817]Sum

试题描述 给定正整数N,R.求 输入 第一行一个数 T,表示有 T 组测试数据. 接下来 T 行,每行两个正整数 n,r. 输出 输出 T 行,每行一个整数表示答案. 输入示例 3 3 5 3 6 3 7 输出示例 3 1 -1 数据规模及约定 对于 100% 的数据,满足 n≤10^9,r≤10^4,T≤10^4. 题解 新技能:类欧几里得算法. #include <iostream> #include <cstdio> #include <cstdlib> #inc