Codeforces Round #740 (Div. 2, based on VK Cup 2021 - Final (Engine))

A. Simply Strange Sort

看数据范围可以得出是一道简单的模拟题 但是坑还挺多的 很多人wa了几次
只有在 对于i等于奇数时候不进行操作 且 对于i等于偶数时候也不进行操作时才能break(感觉像说了一句废话)
刚开始我是只要某一次操作没进行交换就直接break 但是可能偶数操作完没进行交换 下次奇数操作又要进行交换 或者奇数操作完没进行交换 偶数交换完进行了交换
代码

#include <bits/stdc++.h>
 
using namespace std;
 
typedef long long ll;
 
const int N = 1010;
 
ll n, a[N];
 
void solve()
{
	cin >> n;
	for (int i = 1; i <= n; i ++ ) cin >> a[i];
	int tmp = 1, flag1 = 0, flag2 = 0;
	for (int i = 1;; i ++ )
	{
		flag1 = 0;
			for (int j = 1; j <= n - 2; j += 2)
			{
				if (a[j + 1] < a[j]) 
				{
					swap(a[j + 1], a[j]);
					flag1 = 1;
				}  
			}
		if (i != 1 && !flag1 && !flag2)
		{
			tmp -= 2;
			break;
		}
		i ++, tmp ++ ;
		flag2 = 0;
			for (int j = 2; j <= n - 1; j += 2)
			{
				if (a[j + 1] < a[j])
				{
					swap(a[j + 1], a[j]);
					flag2 = 1;
				}
			}
 
		if (!flag1 && !flag2)
		{
			tmp -= 2;
			break;
		}
		tmp ++ ;
	}
	
 
	
	cout << tmp << endl;
}
 
int main()
{
 
	
	int T;
	cin >> T;
	
	while (T -- )
	{
		solve();
	}
	
	return 0;
}

B. Charmed by the Game

题意是两人打网球 轮流发球 如果在对方发球回合赢得比赛则认为是破发 给出a b即两人各赢多少 求破发的可能数量
可以这样看:
10101010101010101 – 发球序列 将a设为1 b设为0 往这个序列里填 如果不相等 则破发的数量加一 例如 a为5, b为7:
1 0 1 0 1 0 1 0 1 0 1 0
1 1 1 1 1 0 0 0 0 0 0 0 破发数量为5
1 0 1 0 1 0 1 0 1 0 0 0 破发数量为1
既然是往里填 又已知 a+b = n 假设a先发可以把1放在左边0放在右边 填a(1)和b(0)
1 1 1 1 1 1 0 0 0 0 0 0
当a或者b大于1的数量时候 例如 a = 8, b = 4
1 1 1 1 1 1 1 1 0 0 0 0
这时候不管怎么填都会有两个0对应这a的1 则我们可以用cur来记录一下 然后删去这两个 即
1 1 1 1 1 1 0 0 0 0
1 1 1 1 1 1 0 0 0 0
然后我们可以通过交换下列的两边的0和1 最多可以交换4次使得上下不同最终为
0 0 0 0 1 1 1 1 1 1 每次交换都会使得破发数量+=2 则记录 最后求解(记得交换一下ab发球先后)
代码

#include <bits/stdc++.h>
 
using namespace std;
 
typedef long long ll; 
 
set<int> s;
 
void deal(int u, int x, int a, int b)
{
	if (u < a)
	{
		swap(u, x);
		swap(a, b);
	}
	int cur = u - a; s.insert(cur);
	b -= cur;
	for (int i = 1; i <= min(a, b); i ++ ) s.insert(cur + i * 2);
}
 
void solve()
{	
	int n, a, b;
	s.clear();
	cin >> a >> b;
	n = a + b;
	int u = n + 1 >> 1, x = n - u;
	
	deal(u, x, a, b);
	deal(u, x, b, a);
	
	cout << s.size() << endl;
	for (auto t : s) cout << t << ' ';
	cout << endl;
}
 
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int T;
	cin >> T;
	
	while (T -- )
	{
		solve();
	}
	
	return 0;
}

C. Deep Down Below

二分加贪心
输入时求出如果想通过当前洞穴所需要的力量最小值以及通过后力量加多少
排一下序 然后每次二分力量 找出所求最小力量
代码

#include <bits/stdc++.h>
 
#define x first
#define y second
 
#define int long long 
 
using namespace std;
 
typedef pair<int, int> PII;
 
const int N = 1e6 + 10;
 
int n, k;
PII g[N];
 
bool check(int u)
{
	for (int i = 1; i <= n; i ++ )
		if (u >= g[i].x) u += g[i].y;
		else return false;
	return true; 
		
}
 
void solve()
{
	cin >> n;
	
	for (int i = 1; i <= n; i ++ )
	{
		int gmin = -1;
		cin >> k;
		for (int j = 1; j <= k; j ++ )
		{
			int u;
			cin >> u;
			gmin = max(gmin, u - j + 2);
		}
		g[i] = {gmin, k};
	}
	
	sort(g + 1, g + n + 1);
	
	int l = 1, r = 1e9 + 10;
	while (l < r)
	{
		int mid = l + r >> 1;
		if (check(mid)) r = mid;
		else l = mid + 1;
	}
	
	cout << r << endl;
}
 
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int T;
	cin >> T;
	
	while (T -- )
	{
		solve();
	}
	
	return 0;
} 


D1 - Up the Strip (simplified version)

题意
从n开始走一直到1 走法1为往上走y步 走法二为走到当前格子除以z的格子 问从n走到1共有多少种方案
方案问题 一般都会想到dp 我们可以将其看作 f[i] 由 f[y] 和 f[i / z] 走到的 则很容易的就能得出暴力的做法

#include <bits/stdc++.h>

#define int long long

using namespace std;

const int N = 2e5 + 10;

int n, p;
int f[N];

signed main()
{
	scanf("%lld%lld", &n, &p);
	
	f[1] = 1;
	for (int i = 2; i <= n; i ++ )
	{
		for (int j = 1; j < i; j ++ )
			f[i] += f[j];
		for (int j = 2; j <= i; j ++ )
			f[i] += f[i / j];
	}
	
	cout << f[n] %  << endl;
	
	return 0;
}

很明显会超时 所以我们就需要想一想怎样优化
对于走法1 我们可以用一个res来维护从1到i-1所有f[j]的前缀和 则可以O(1)解决
但是对于走法2呢 分析一下
f[i] = f[i / 2] + f[i / 3] + f[i / 4] + … + f[i / i]
由此可以想到 数论分块 我们再给他加一个 f[i/1] 且f[i]初始时为0 所有随便加
然后用数论分块来优化为O(sqrt(n))
代码

#include <bits/stdc++.h>
 
#define int long long
 
using namespace std;
 
const int N = 2e5 + 10;
 
int n, p;
int f[N];
 
signed main()
{
	scanf("%lld%lld", &n, &p);
	
	f[1] = 1;
	int res = f[1];
	for (int i = 2; i <= n; i ++ )
	{
		int cnt = 0;
		for (int l = 1, r; l <= i; l = r + 1)
		{
			r = i / (i / l);
			cnt = (cnt + (r - l + 1) * f[i / l]) % p;
		}
		f[i] = cnt % p;
		f[i] = f[i] + res, res = res + f[i];
		f[i] %= p, res %= p;
	}
	
	cout << f[n] % p << endl;
	
	return 0;
}

关于数论分块 这有两篇大佬的讲解
数论分块
数论分块

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值