Codeforces Round #740 (Div. 2, based on VK Cup 2021 - Final (Engine)) (A~D1)题解

比赛链接

官方题解链接


1561A Simply Strange Sort 题目链接:点击这里传送

在这里插入图片描述
题意:
现给出一串序列,要将其变成升序。关于一趟排序的定义是

for (int i = 1; i <= n-2; i += 2)
{
	if (a[i] > a[i + 1]) swap(a[i], a[i + 1]);
}

或者

for (int i = 2; i <= n-1 ; i += 2)
{
	if (a[i] > a[i + 1]) swap(a[i], a[i + 1]);
}

问最少要几趟。
思路:
题目咋说就咋做

#include<bits/stdc++.h>
using namespace std;
#define MAXN 1005
int a[MAXN];
int n, t;
bool check()
{
	for (int i = 1; i <= n; i++)
	{
		if (a[i] != i) return 0;
	}
	return 1;
}
int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> t;
	while (t--)
	{
		memset(a, 0, sizeof(a));
		cin >> n;
		int cnt = 0;
		for (int i = 1; i <= n; i++) cin >> a[i];
		while (1)
		{
			if (check() == 1) break;
			for (int i = 1; i <= n-2; i += 2)
			{
				if (a[i] > a[i + 1]) swap(a[i], a[i + 1]);
			}
			cnt++;
			if (check()==1) break;
			for (int i = 2; i <= n-1 ; i += 2)
			{
				if (a[i] > a[i + 1]) swap(a[i], a[i + 1]);
			}
			cnt++;
		}
		cout << cnt << endl;
	}

	return 0;
}

1561B Charmed by the Game 题目链接:点击这里传送

在这里插入图片描述
题意:
给出一场乒乓球比赛的最终得分,求出所有可能的破发球情况。
思路:
第一视角解说
破发球次数=我方破发球次数+我方被破发球次数
总得分为偶数,我方发球次数=sum ÷ \div ÷ 2
总得分为奇数,我方发球次数=sum ÷ \div ÷ 2或者sum ÷ \div ÷ 2+1。
我方破发球次数=我方得分-我方成功发球次数
我方被破发球次数=我方发球次数-成功发球次数
我方得分已知,我方发球次数已知,只需枚举我方成功发球次数即可。
需要注意的是,我方得是得分少的那个,这样才能确保成功发球次数。如果用得分多的,假设他的分数超过了我方发球次数,那么就不能通过 [ 0 , 我 方 得 分 ] \left[0,我方得分\right] [0,]来枚举我方发球成功次数了。

#include<bits/stdc++.h>
using namespace std;
set <int> s;
int n, m,t;
int sum;

int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> t;
	while (t--)
	{
		s.clear();
		cin >> n >> m;
		sum = n + m;
		if (n > m) swap(n, m);
		//破发球次数=我方破发球次数+我方被破发次数
		//我方破发球次数=我方分数-发球成功次数   n-i
		//我方被破发次数=发球总数-成功发球次数	  sum/2-i或sum/2+1-i
		//发球次数 偶数 sum/2 奇数sum/2+1或sum/2  枚举发球成功次数
		if (sum % 2 == 0)
		{
			for (int i = 0; i <= n; i++) s.insert(n - i + sum / 2 - i);
		}
		else
		{
			for (int i = 0; i <= n; i++)
			{
				s.insert(n - i + sum / 2 - i);
				s.insert(n - i + sum / 2 + 1 - i);
			}
		}
		cout << s.size() << endl;
		for (auto x : s) cout << x << " ";
		cout << endl;
	}
	return 0;
}

1561C Deep Down Below 题目链接:点击这里传送

在这里插入图片描述
题意:
你是一名战士,你要去打怪升级。你有初始攻击力为n点,假设怪物有m点护甲值,只有当n>m时才能打得过怪并且获得+1攻击力,否则你就死了。现在你要去刷副本,一个副本内必须按照给定的怪物顺序一个一个杀过去。你可以任意挑选挑战副本的顺序。现在你有一个修改器,可以调整初始攻击力,问最少需要多少初始攻击力才能打通所有副本。
思路:
设一个副本内怪物的攻击力为 a i a_i ai,需要
a 1 + 1 a_{1}+1 a1+1, a 2 + 1 − 1 a_{2}+1-1 a2+11, a 3 + 1 − 2 a_3+1-2 a3+12+……+ a n + 1 − ( n − 1 ) a_n+1-(n-1) an+1(n1)点攻击力。把这其中最高的攻击力拿出来就是通关这个副本需要的最小攻击力,设他为 b i b_i bi
将b数组进行升序排序。进行模拟找出通关所有副本的最小初始攻击力。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t, n,m;
#define MAXN 100005
ll a[MAXN];//临时处理数据用
struct node
{
	ll val;//打通副本需要的最小初始攻击力
	int num;
}b[MAXN];
bool cmp(node a, node b) { return a.val < b.val; }
int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> t;
	while (t--)
	{
		cin >> n;
		for (int k = 1; k <= n; k++)//初始攻击力要尽可能小
		{
			cin >> m;
			ll temp = 0;
			for (int i = 1; i <= m; i++)
			{
				cin >> a[i];
				a[i] = a[i] - (i - 1);
				temp = max(temp, a[i]+1);
			}
			//cout << "进这个本需要" << " " << temp << endl;
			b[k].val = temp;
			b[k].num = m;
		}
		sort(b + 1, b + 1 + n,cmp);//从易到难给副本难度排序
		ll ans = 0;
		ll num = 0;
		ll current_attack = 0;
		for (int i = 1; i <= n; i++)
		{
			if (current_attack >= b[i].val)
			{
				current_attack += b[i].num;
				//cout << current_attack << ">=" << b[i].val << endl;
			}
			else//攻击力不够,要更新ans
			{
				//cout << "需要" << b[i].val << "初始力量,减去" << num << endl;
				ans = ans + (b[i].val - current_attack);
				current_attack = b[i].val+b[i].num;
			}
			num += b[i].num;
		}
		cout << ans << endl;
	}
	return 0;
}

1561D1 Up the Strip (simplified version) 题目链接:点击这里传送

在这里插入图片描述
题意:
有一个数n,你要将他变成1.你可以把他变成n/2,也可以将他变成n-1,求有多少种操作的方法能将n变为1.
思路:
设dp [ i ] \left[i\right] [i]表示i变为1的方法数。初始化dp [ 1 ] \left[1\right] [1]=1.
状态转移方程: d p [ i ] = d p [ i − x ] + d p [ i / x ] dp\left[i\right]=dp\left[i-x\right]+dp\left[i/x\right] dp[i]=dp[ix]+dp[i/x],枚举x直接暴力,时间复杂度肯定不够
d p [ i − x ] dp\left[i-x\right] dp[ix]的和这个式子可以用前缀和表示
∑ x = 2 i d p [ i / x ] = d p [ i / 2 ] + d p [ i / 3 ] + d p [ i / 4 ] + … … + d p [ i / i ] \sum_{x=2}^i{dp\left[i/x\right]}=dp\left[i/2\right]+dp\left[i/3\right]+dp\left[i/4\right]+……+dp\left[i/i\right] x=2idp[i/x]=dp[i/2]+dp[i/3]+dp[i/4]++dp[i/i]

d p [ i / 2 ] + d p [ i / 3 ] + d p [ i / 4 ] + … … + d p [ i / i ] = d p [ i / 1 ] + d p [ i / 2 ] + d p [ i / 3 ] + d p [ i / 4 ] + … … + d p [ i / i ] − d p [ i / 1 ] dp\left[i/2\right]+dp\left[i/3\right]+dp\left[i/4\right]+……+dp\left[i/i\right]=dp\left[i/1\right]+dp\left[i/2\right]+dp\left[i/3\right]+dp\left[i/4\right]+……+dp\left[i/i\right]-dp\left[i/1\right] dp[i/2]+dp[i/3]+dp[i/4]++dp[i/i]=dp[i/1]+dp[i/2]+dp[i/3]+dp[i/4]++dp[i/i]dp[i/1]
这个式子可以用整除分块来做。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 200005
ll n, mod;
ll dp[MAXN];//表示i到1有几种方法
ll sum[MAXN];//dp数组的前缀和
//dp[i]=dp[i-1]+dp[i/2]
//可以减去x,可以除以x
int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> n >> mod;
	dp[1] = 1;
	sum[1] = dp[1];
	for (int i = 2; i <= n; i++)
	{
		dp[i] = (dp[i] + sum[i - 1]) % mod;
		//dp[i/2]+dp[i/3]+...+dp[i/i]=dp[i/1]+dp[i/2]+dp[i/3]+...+dp[i/i]-dp[i/1]
		ll substract = dp[i];
		ll ans = 0;
		for (int l = 1, r; l <= i; l = r + 1)//整除分块
		{
			r = i / (i / l);
			ans += ((r - l + 1) * (dp[(i / l)%mod])%mod);
		}
		dp[i] =(dp[i]+ ans - substract)%mod;
		sum[i] = (sum[i - 1] + dp[i]) % mod;
	}
	cout << dp[n];
	return 0;
}

D2普通的整除分块也不行,得把 O ( s q r t n ) O(sqrtn) O(sqrtn)的复杂度降到 O ( l o g n ) O(logn) O(logn),不会。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值