Codeforces Round #561 (Div. 2) : D. Cute Sequences (数学 + 构造)

27 篇文章 0 订阅

题目大意:输入一个m,定义一个序列,其中 Xn = Xn - 1 + Xn - 2 + … + X1 + R, 其中 1 <= R <= m。输入a,b,m ,让你构造出以a为第一项,b为最后一项,满足上述要求的序列。1 <= a <= b <= 1e14, 1 <= m <= 1e14。

分析:项数不会超超过50项,假设 a = 1, b = 1e14, 每次 r = 0,最后一项为 2 ^ 49 次方,已经超过 1e14。

先看答案不存在的情况:由定义式子可得:
X1 = X1;
X2 = X1 + R1;
X3 = X2 + X1 + R2;

逐项代入得:Xn = pow(2,n - 2) * X1 + pow(2,n -3) * R1 + pow(2,n - 4) * R2 + … + 2 * Rn-3 + Rn-2 + Rn-1。
显然Xn的最小值是所有R都取1的情况,最大值是所有R都取m的情况:这时后面是一个等比数列求和,可以计算得到 最小值:Xn = pow(2,n - 2) * (a + 1),最大值 Xn = pow(2,n - 2) * (a + m)。

如果b 在这个值之间,就存在答案,可以用二分去搜索 n 使得最后一个 pow(2,n - 2) * (a + 1) <= b 成立。再判断一下 pow(2,n - 2) * (a + m) >= b 是否成立。

当有答案时:如何构造得到答案?
观察式子:Xn = pow(2,n - 2) * X1 + pow(2,n -3) * R1 + pow(2,n - 4) * R2 + … + 2 * Rn-3 + Rn-2 + Rn-1。
可以先取所有的R都等于某以个数值,这样Xn = pow(2,n - 2) * a + pow(2 , n - 2) * t,取Xn <= b 时t尽量大的值。之后所有的R只需要在当前这个序列上调整, 因为 pow(2,n - 2) * (a + 1) <= b <= pow(2,n - 2) * (a + m) , 所有的R取t后 R 和Xn的差值 肯定小于 pow(2,n - 2),Xn的一般式子中 后面一串刚好是一串最大位权到 pow(2,n -3)的二进制,二进制一定能表示出在这范围内的所有整数,所以一定能构造出。计算出b的二进制数值,每一位加上对应增量即可。

会爆long long,可以用除法和double

#include<bits/stdc++.h>
using namespace std;
int q;
long long m,a,b;
long long fpow(long long a,long long b) {
	long long res = 1;
	while(b) {
		if(b & 1) res *= a;
		a *= a;
		b >>= 1;
	}
	return res;
}
long long rr[100];
long long pp[100];
int main() {
	scanf("%d",&q);
	while(q--) {
		scanf("%lld%lld%lld",&a,&b,&m);
		memset(pp,0,sizeof pp);
		memset(rr,0,sizeof rr);
		if(a == b) {
			printf("1 %lld\n",a);
			continue;
		}
		int l = 0,r = 51;
		long long ans,res;
		while(l < r) {
			int mid = l + r >> 1;
			long long tmp = fpow(2,mid);
			if(tmp  > b / (a + 1)) r = mid;
			else {
				l = mid + 1;
				ans = mid;
			}
		}
		if((a + m) < (double) b / fpow(2,ans)) {
			puts("-1");
			continue;
		}
		b -= fpow(2,ans) * a;
		long long p = fpow(2,ans);
		l = 1,r = m;
		res = b / p;
		b -= res * p;
		for(int i = 1; i <= ans; i++) {
			long long d = fpow(2,ans - i);
			long long z = b / d;
			b -= z * d;
			rr[i] = z + res;
		}
		rr[ans + 1] = res;
		pp[1] = a;
		printf("%d ",ans + 2);
		for(int i = 2; i <= ans + 2; i++) {
			pp[i] = fpow(2,i - 2) * a;
			for(int j = 1; j <= i - 2; j++) {
				pp[i] += fpow(2,i - 2 - j) * rr[j];
			}
			pp[i] += rr[i - 1];
		}
		for(int i = 1; i <= ans + 2; i++) {
			printf("%lld",pp[i]);
			if(ans + 2 - i) printf(" ");
		}
		puts("");	
	}
	return 0;
}

小心得:第二次做这种偏向数学思维的构造了,都是先构造出一个基本的,再去调整。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值