2021-2022年度第三届全国大学生算法设计与编程挑战赛(冬季赛)-正式赛 部分题解

B.Error

思路

首先考虑二分答案偏移量,判断的时候我们可以考虑贪心策略,对于两个相邻的点 i i i, i + 1 i + 1 i+1。我们很容易知道对应的 b [ i ] , b [ i + 1 ] b[i],b[i +1] b[i],b[i+1]的取值范围分别为 [ a [ i ] − e p s , a [ i ] + e p s ] , [ a [ i + 1 ] − e p s , a [ i + 1 ] + e p s ] [a[i]-eps, a[i]+eps],[a[i + 1]-eps,a[i + 1]+eps] [a[i]eps,a[i]+eps],[a[i+1]eps,a[i+1]+eps]为了保证我们的数组 b b b是单调递增的,我们首先应该判断是否 a [ i + 1 ] + e p s < = a [ i ] − e p s a[i+1]+eps<=a[i]-eps a[i+1]+eps<=a[i]eps,如果满足的话,就退出。否则的话我们肯定让当前的 b [ i ] b[i] b[i]尽量小,所以有 b [ i ] = m a x ( b [ i − 1 ] + 1 , a [ i ] − e p s ) b[i]=max(b[i-1]+1,a[i]-eps) b[i]=max(b[i1]+1,a[i]eps),然后继续判断即可。

代码

#include
using namespace std;
#define ll long long
ll n,m,k;
ll a[51];
ll b[51];
ll check(ll x)
{
		a[0] -= x;
		for(int i = 1;i < n;i ++){
			if(a[i] + x <= a[i - 1]) return 0;
			a[i] = max(a[i-1] + 1,a[i] - x);
		}
		return 1;
}
int main()
{
	cin >> n;
	for(int i = 0; i < n; i ++ ) cin >> a[i];
	ll l = 1,r = 2e9;
	for(int i = 0; i < n; i ++ ) b[i] = a[i];
	while(l<=r){
	for(int i = 0; i < n; i ++ ) a[i] = b[i];
		ll mid = (l + r) >> 1;
		if(check(mid)) r = mid - 1;
		else l = mid + 1;
	}
	cout << l << "\n"; 
}

E.吃利息

思路

根据题目模拟即可。

代码

#include 
using namespace std;
#define ll long long
int n, k;
ll ans1, ans2;
int main()
{
	cin >> n >> k;
	ans1 = n;
	while(k -- )
	{
		if(ans1 >= 10)
		{
			ll p = ans1 / 10;
			if(p > 5) p = 5;
			ans1 += p;
		}
		ans1 += 5;
	}
	cout << ans1 << endl;
}

G MP4

思路

首先注意到 n n n的范围非常大,达到了 1 0 9 10^9 109。所以我们不可能把所有的字符串都存下来,进行排序。注意到我们相当于是求序列在字典序的情况下的前 k k k位,所以可以研究一下序列的性质。我们发现,对于每一位,我们知道小的肯定比大的优先级更高,再者,对于某两个共有p位的数 a , b a,b a,b满足 a ≤ b a\le b ab,且第 p p p位以前的数都相同,那么我们知道对于任意一个有 q ( q > p ) q(q>p) q(q>p)位的数,前 p p p为与 a a a相同的数一定比前 p p p为与 b b b相同的数优先级更高。所以我们可以考虑使用 d f s dfs dfs的方法来解决这个问题。我们先考虑第一位上的数字,然后以此为基础按照字典序枚举第一位为该数字的满足题意要求的数。

代码

#include
using namespace std;

long long n,m,k;
int p = 50;
void output(int x)
{
	printf("%d.mp4\n", x);
}
void dfs(int x, int y)
{
	if(x > n) return;
	output(x);
	p --;
	if(!p) return;
	for(int i = 0;i < 10;i ++ ){
		dfs(x * 10 + i, 0);
		if(p == 0) return;
	}
}
int main()
{	
	cin >> n;
	for(int i = 1;i < 10; i ++ ) {
		if(p)
		dfs(i, 0);
	}
}

I 展览

思路

按照注释填写即可。

代码

#include
#define ll long long
using namespace std;
const ll N=1e6+5;
ll n,b[N],a[N],ans;
//b[i]表示二进制下的第i位
void update(ll x){
	for(ll i = 60;i >= 0;i -- ){
		if((x >> i) & 1){//如果x在二进制表示下含有第i位
			if(b[i]) x ^= b[i];//如果b[i]存在则让x^b[i],
			//因为之前b[i]也是由已经保存过的a[]数组贡献的
			//所以,这样异或x可以看作x于之前的a[]数组进行异或
			//然后一直异或到为0或者当前b[i]还没有被赋值
			else {b[i] = x;break;}//否则b[i]赋值为x,
			//表示当前二进制下的第i位可以被异或出来,且x的最高位就是i
		}
	}
}

int main(){
	cin >> n;
	for(ll i = 1;i <= n; i ++ ){cin >> a[i]; update(a[i]);}//读入数据对于每一个数字都下放来维护b[i]
	for(ll i = 60;i >= 0; i -- )if((ans ^ (1ll << i)) > ans) ans ^= b[i];
	//贪心的过程,ans看作一个二进制数,从高位开始,如果b[i]存在,
	//肯定优先跟b[i]异或,倒着让小值不会影响到大值
	cout << ans<< endl;
	return 0;
}

K礼物

思路

求最大值即可。

代码

#include 
using namespace std; 
int n, m;
int ans = 0;
int main()
{
	cin >> n;
	for(int i = 1; i <= n; i ++ )
	{
		int x;
		cin >> x;
		ans = max(ans, x);
	}
	cout << ans << endl;
	return 0;
}

L看错题

思路

题目告诉了我们这是一棵完全二叉树,也就是说每一个非叶子节点一定有且仅有两个儿子节点。我们考虑每次求和,根据线段树的性质我们知道,线段树每一层节点之和是相等的,又因为一个父亲节点有且仅有两个儿子节点,所以求和的时候每一个节点会被儿子节点经过两次,所以如果最下面一层的贡献为 s u m sum sum,那么倒数第二层的贡献一定为 2 s u m 2sum 2sum,以此类推,根节点的贡献即为 2 l o g ( k ) + 1 2^{log(k)+1} 2log(k)+1。所以总贡献即为 s u m + 2 s u m + 4 s u m + . . . + 2 l o g ( k ) + 1 = ( 2 n − 1 ) s u m sum+2sum+4sum+...+2^{log(k)+1}=(2n-1)sum sum+2sum+4sum+...+2log(k)+1=(2n1)sum。所以只记录最底层的贡献 s u m sum sum即可,时间复杂度为 O ( m ) O(m) O(m)

代码

#include
using namespace std;
#define ll long long
const int N = 1e6 + 10;
ll n,m,k;
ll a[N];
int main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; i ++ ) cin >> a[i];
	ll sum = 0;
	for(int i = 1; i <= n; i ++ )
	{
		sum += a[i];
	} 
	while(m--){
		int l, r, x;
		cin >> l >> r >> x;
		sum += x * (r - l + 1);
		cout << sum * (2 * n - 1) << endl;
	}

你可能感兴趣的:(编辑器,算法)