Codeforces883(Div.3) E1+E2

E1. Rudolf and Snowflakes (simple version)

题目传送门:

https://codeforces.com/contest/1846/problem/E1

题意:有一个顶点,有k个新顶点与其相连接,可执行无限次以下操作:让最外的每个顶点通过边与k个新顶点相连接(此操作至少做一次),问是否存在k在执行上述操作后,使得顶点总和为n。(数据范围:1e6)

思路:由题意可知,每个图形最初组成至少有1+i+i*i个顶点,1次操作后,会增加(i*i)*i个顶点,2次操作后,会增加(i*i*i)*i个顶点......n次操作后会增加(i*i)*i^n个顶点。由于本题数据较小,可以一次遍历,开一个bool数组,把所有可能的k都枚举一边,再把对应经过相应次数的操作所有的顶点和标记一下,最后再查询n是否被标记即可。

代码显示如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int,int>PII;
const int N=1e6+10,INF=1e9;
int n;
bool st[N];
void solve()
{
	cin>>n;
	if(st[n]) cout<<"YES"<<endl;
	else cout<<"NO"<<endl;
}

signed main() {

	for(int i=2;i<=1000;i++)
	{
		int sum=1+i+i*i;//最小雪花所需顶点
		int mul=i*i*i;
		while(sum<=1000000)
		{
			st[sum]=true;
			sum+=mul;
			mul*=i;
		}
		
	}
	int t;
	cin>>t;
	while(t--)
	{
		solve();
	}
}

E2. Rudolf and Snowflakes (hard version)

题目传送门:

https://codeforces.com/contest/1846/problem/E2

题意:与E1题意一样,但数据范围不同(1e18)

思路:遍历层数,等比数列求和,二分。

首先按层数遍历,层数最小是三,有题意可知k最小值是2,以2为公比的等比数列的求和公式是(2^x-1)要小于等于1e18,此时这个x代表层数最大值,求得最大层数近似64,所以层数x的范围是3~64。接下来就是求对每个层数对应的顶点总和是否与n相等:

已知等比数列的求和公式是:n=(k^x-1)/(k-1)

x即是对应层数,现在目的就是求k,若直接利用求和公式开根号求解会存在精度问题,所以为了得到准确k就需要进行二分。

代码显示如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int, int>PII;
const int N = 1e6 + 10, INF = 1e9;
int n, x;
bool st[N];
/*__int128 就是占用128字节的整数存储类型。由于是二进制,范围就是 -2^127~2^127-1,如果使用了 unsigned __int128,则范围变成 00 ~ 21282128,即约39位数,这在一定程度上可以替代高精度运算实现大数运算,而且操作难度更低,
  所以在数据范围不超过的情况下,都可以使用__int128。*/
//sum<=(k^n-1)/(k-1)-->k*n>=sum*(k-1)+1
//存在精度问题要用二分
bool check(int x) {
	int l = 2, r = 1e9;
	while (l <= r) {
		int mid = (l + r) / 2;
		__int128 po = 1;
		
		__int128 t = (__int128)n * (mid - 1) + 1;
		for (int i = 0; i < x; i++) {//若(po==k*n)>(sum*(k-1)+1==t)则不满足,break
			po *= mid;
			if (po > t) break;
		}
		//正常情况下要将上个循环循环完整即sum=(k^x-1)/(k-1)则为存在k,返回true
		if (po == t) return true;

		if (po > t) r = mid - 1;
		else l = mid+1;
	}
	return false;
}

void solve() {
	cin >> n;
	
	for (int i = 3; i <= 60; i++) { //遍历层数
		if (check(i)) {
			cout << "YES" << endl;
			return;
		}
	}
	cout << "NO" << endl;
}

signed main() {

	int t;
	cin >> t;
	while (t--) {
		solve();
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值