Codeforces Round #681 (Div. 2, based on VK Cup 2019-2020 - Final)

比赛连接:https://codeforces.com/contest/1443

A - Kids Seating

题意:

n个人要坐座位,一共4*n个座位,要求两个人的座位号不能互质,且不能一个座位号能被
另一个人的座位号整除,要求输出n个人的座位号。


思路:

首先输出n*2,然后每次加2,满足公约数是2,且因为最大的是2*n-2,所以也不会出现整除的情况。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
typedef long long ll;

int main()
{
#ifdef LOCAL
	freopen("E:/input.txt", "r", stdin);
#endif
	int t;
	cin >> t;
	while (t--)
	{
		int n;
		cin >> n;
		int num = n * 2;
		for (int i = 1; i <= n; i++)
		{
			cout << num;
			num += 2;
			printf("%c", i == n ? '\n' : ' ');
		}
	}
	return 0;
}

B - Saving the City

题意:

给出n个位置,每个位置是0或者1,1表示有炸弹,引爆一个炸弹会导致相邻的炸弹爆炸,
两种操作,点爆一个炸弹或者在一个0的位置安防一个炸弹,询问引爆所有炸弹的最小代价。

思路:

设d[i][2]表示引爆第i堆答案是连着上一堆一起点爆的,还是单独点爆的。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
typedef long long ll;

char s[N];
int d[N][2];
int main()
{
#ifdef LOCAL
	freopen("E:/input.txt", "r", stdin);
#endif
	int t;
	cin >> t;
	while (t--)
	{
		int a, b;
		cin >> a >> b;
		scanf("%s", s + 1);
		int len = strlen(s + 1);
		int cnt = 0, flag = 0;
		ll ans = 0;
		int x = 0;
		for (int i = 1; i <= len; i++)
		{
			if (s[i] == '0')
				++cnt;
			else
			{
				x++;
				d[x][0] = min(d[x - 1][1] + a, d[x - 1][0] + a);
				if (x == 1)
					d[x][1] = d[x][0];
				if (x >= 2)
					d[x][1] = min(d[x - 1][1] + cnt * b, d[x - 1][0] + cnt * b);
				cnt = 0;
			}
		}
		cout << min(d[x][0], d[x][1]) << endl;
	}
	return 0;
}

C - The Delivery Dilemma

题意:

给出n个饭店的送饭时间,最开始你可以让一些饭店送到你家,或者你去取
但是你取的饭店时间是相加的,如果是送的话,几家饭店是可以同时送的。

思路:

枚举每个饭店的送餐时间作为最大值的情况,剩余的饭店自己去取,取一个min即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
typedef long long ll;

struct node
{
	ll x, y;
	bool operator < (const node& oth)const
	{
		return x < oth.x;
	}
}a[N];
ll sum[N];
int main()
{
#ifdef LOCAL
	freopen("E:/input.txt", "r", stdin);
#endif
	int t;
	cin >> t;
	while (t--)
	{
		ll sum = 0;
		int n;
		scanf("%d", &n);
		ll ans = 0;
		for (int i = 1; i <= n; i++)
			scanf("%lld", &a[i].x), ans = max(ans, a[i].x);
		for (int i = 1; i <= n; i++)
			scanf("%lld", &a[i].y), sum += a[i].y;
		sort(a + 1, a + n + 1);
		ans = min(ans, sum);
		for (int i = 1; i <= n; i++)
		{
			ans = min(ans, max(a[i - 1].x, sum));
			sum -= a[i].y;
		}
		cout << ans << endl;
	}
	return 0;
}

D - Extreme Subtraction

题意:

给出一个数组,两种操作,选某个前缀每个数字减一,或者选某个后缀的每个数字减一,
询问最终能不能把所有数字都变成0。

思路:

如果这个序列是一个递增序列,我们可以仅仅对后缀多次操作,即可全部变成0。
考虑怎么使用前缀的操作使得这个序列满足是递增序列。如果倒着来,当前的比
上一个小,则不用管了,如果大了,说明要使用前缀操作,那么在这个位置以及
之前的位置都要使用前缀操作,这个时候记一个偏移量,如果前面的某个位置减
到负了,说明不满足条件了,就输出NO。若都满足则输出YES。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
typedef long long ll;

int a[N];
int mi1[N];
int mi2[N];
int main()
{
#ifdef LOCAL
	freopen("E:/input.txt", "r", stdin);
#endif
	int t;
	cin >> t;
	while (t--)
	{
		int n;
		scanf("%d", &n);
		for (int i = 1; i <= n; i++)
			scanf("%d", &a[i]);
		int off = 0, flag = 1;
		for (int i = n - 1; i >= 1; i--)
		{
			a[i] -= off;
			if (a[i] < 0)
			{
				flag = 0;
				break;
			}
			if (a[i] > a[i + 1])
				off += a[i] - a[i + 1], a[i] -= a[i] - a[i + 1];
		}
		puts(flag ? "YES" : "NO");
	}
	return 0;
}

E - Long Permutation

题意:

有一个长度为n的排列,最开始是1~n,有q次操作,
1号操作询问区间[l,r]的sum,第二种操作是当前排列变换到比其字典序大x的排列。

思路:

询问次数2e5,x最大1e5,一共2e10。
14的阶乘大概8e10,也就是说最多变换后面的14位,我们可以使用逆康拓展开求出当前要求的序列。
然后暴力更新答案就行了,时间复杂度是O(q*(14^2))。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
typedef long long ll;

int a[N];
ll fac[30];
ll sum[N];
void ReContor(int *a, int n, ll k, int ed)
{
	int vis[30] = { 0 };
	for (int i = 1; i <= n; i++)
	{
		int cnt = k / fac[n - i], j;
		for (j = 1; j <= n; j++) //未使用的数字中第cnt大的
			if (!vis[j])
			{
				if (!cnt) //找到数字
					break;
				cnt--;
			}
		vis[j] = 1;
		a[i] = ed + j;
		k %= fac[n - i]; //对当前阶乘取模
	}
}
void init()
{
	fac[0] = 1;
	for (int i = 1; i <= 20; i++)
		fac[i] = fac[i - 1] * i;
}
int main()
{
#ifdef LOCAL
	freopen("E:/input.txt", "r", stdin);
#endif
	init();
	int n, q;
	scanf("%d%d", &n, &q);
	for (int i = 1; i <= n; i++)
		sum[i] = sum[i - 1] + i;
	int m = 14, ed = n - 14;
	if (n < 14)
		m = n, ed = 0;
	for (int i = 1; i <= m; i++)
		a[i] = i;
	ll cnt = 0;
	while (q--)
	{
		int op;
		scanf("%d", &op);
		if (op == 1)
		{
			int l, r;
			scanf("%d%d", &l, &r);
			printf("%lld\n", sum[r] - sum[l - 1]);
		}
		else
		{
			int x;
			scanf("%d", &x);
			cnt += x;
			ReContor(a, m, cnt, ed);
			for (int i = 1; i <= m; i++)
				sum[ed + i] = sum[ed + i - 1] + a[i];
			for (int i = 1; i <= m; i++)
				a[i] = i;
		}
	}
	return 0;
}

F - Identify the Operations

题意:

给出一个长度为n的序列,可以删除a序列的某个数字,然后把左右两个数字任选其一
复制加到空序列b里,给出长度为m的b序列询问有多少种方案满足能得到b序列。

思路:

记录位置,遍历b序列,看下每个数字在a序列左右的数字,如果不是b序列中的,则可以
删除,如果是b序列中的某个数字,但是位置在其之前,因为已经放到b集合了则也可以删除,否
则不能删除,累乘计算答案就可以了。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int mod = 998244353;
typedef long long ll;

int a[N];
int b[N];
int pos1[N];
int pos2[N];
int vis[N];
int main()
{
#ifdef LOCAL
	freopen("E:/input.txt", "r", stdin);
#endif
	int t;
	cin >> t;
	while (t--)
	{
		int n, m;
		cin >> n >> m;
		for (int i = 1; i <= n; i++)
			scanf("%d", &a[i]), pos1[a[i]] = i, vis[i] = 0;
		for (int i = 1; i <= m; i++)
			scanf("%d", &b[i]), pos2[b[i]] = i, vis[b[i]] = 1;
		ll ans = 1;
		for (int i = 1; i <= m; i++)
		{
			int cnt = 0;
			if (pos1[b[i]] - 1 >= 1 && (!vis[a[pos1[b[i]] - 1]] || pos2[b[i]] > pos2[a[pos1[b[i]] - 1]]))
				cnt++;
			if (pos1[b[i]] + 1 <= n && (!vis[a[pos1[b[i]] + 1]] || pos2[b[i]] > pos2[a[pos1[b[i]] + 1]]))
				cnt++;
			ans = ans * cnt % mod;
		}
		cout << ans << endl;
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值