P1147 连续自然数和

P1147 连续自然数和

在这里插入图片描述

https://www.luogu.com.cn/problem/P1147

P1147 连续自然数和_第1张图片

解法1:数学方法

设首项为 L, 末项为 R,那么 s u m ( L , R )   =   ( L   +   R ) ( R   −   L   + 1 ) 2   =   M sum(L,R)\, =\, \frac {(L\, +\, R)(R\, -\, L\, +1)} {2}\, =\, M sum(L,R)=2(L+R)(RL+1)=M

即(L + R)(R - L + 1) = 2M

把2M分解成两个数的乘积,枚举假设分成了 x 1 {x}_{1} x1, x 2 {x}_{2} x2 ( x 1 {x}_{1} x1 < x 2 {x}_{2} x2)

可得到一个二元一次方程组
{ L = x 2 − x 1 + 1 2 R = x 1 + x 2 − 1 2 \begin{cases} L=\dfrac{x_2-x_1+1}{2}\\ R=\dfrac{x_1+x_2-1}{2} \end{cases} L=2x2x1+1R=2x1+x21

显然 x 1 {x}_{1} x1, x 2 {x}_{2} x2 一奇一偶时,L,R才是整数

但是L = R的情况是不允许的

x 1 {x}_{1} x1 ≠ 1

int m;
int main()
{
	cin >> m;
	for(int k1 = sqrt(2 * m); k1 > 1; k1 -- ) //枚举k1,注意是k1 > 1
	{
		if(2 * m % k1 == 0 && (k1 + 2 * m / k1) % 2 ) //如果k2是整数且与k1一奇一偶
		{
			int k2 = 2 * m / k1;
			cout << (k2 - k1 + 1) / 2 <<" "<<(k1 + k2 - 1) / 2 << endl;//输出答案
		}
	}
}

尺取法(双指针)

用i, j 表示区间的左右端点

当sum小于目标值M时,将右端点右移(J ++),sum会变大

当sum小于目标值M时,将左端点右移(I ++),sum会变小

在双指针运动的过程中,如果出现sum == M的情况就输出

因为两个指针都是单调向右移动,也只扫一遍,所以时间复杂度是O(n)

左端点大于m/2的时候即可停止,因为长度为2的连续序列和一定大于M

int m;
int main()
{
	cin >> m;
	int sum = 3;
	for(int i = 1, j = 2; i <= m / 2;
	{
		if(sum == n) 
		{
			printf("%d %d\n",i, j);
			sum -= i; i ++ 
		}
		else if(sum < m)
		{
			j ++ ; sum += j;
		}
		else 
		{
			sum -= i; i ++ ;
		}
	}
return 0;
}

前缀和 + 二分

lower_bound ()函数. lower_bound () 函数用于在指定区域内查找不小于目标值的第一个元素

通过前缀和和二分可以在O(nlogn)的时间里面完成

int main()
{
	int n;
	cin >> n;
	for(int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + i;
	for(int i = 1; i <= n / 2; i ++ )
	{
		int mid = s[i - 1] + n;
		int a = lower_bound(s, s + n + 1, mid) - s;
		if(s[a] - s[i - 1] == n)
			if(i != a) cout << i << " " << a << endl;
	}
	return 0;
}

你可能感兴趣的:(洛谷OJ题解,经验分享,c++,c语言)