Codeforces Round 894 (Div. 3)

Dashboard - Codeforces Round 894 (Div. 3) - Codeforces

又有闲工夫写题解喽w,虽然写完感觉讲的不是很清楚

成功倒开的一集,但是写太慢了差两题AK

A. Gift Carpet

题意:

给出一个由小写字母构成的矩阵,问举证是否满足一下条件:选择四列a,b,c,d(a<b<c<d)使得四列中分别包含'v','i','k','a'

题解:

暴力

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef long double LD;
const LL N = 2e1 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
char ch[N][N], s[] = "vika";
void solve()
{
	int n, m, step = 0;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i)
		scanf("%s", ch[i] + 1);
	for (int i = 1; i <= m; ++i)
	{
		for (int j = 1; j <= n; ++j)
		{
			if (ch[j][i] == s[step])
			{
				++step;
				break;
			}
		}
		if (step == 4)
		{
			printf("YES\n");
			return;
		}
	}
	printf("NO\n");
}
int main()
{
	int T = 1;
	scanf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

B. Sequence Game

题意:

对于数组a做以下操作:1,输出a1。2、对于所有2<=i<=n,若ai-1<=ai输出ai。

给出输出后的数组b,求一个原数组ai。

题解:

若ai-1>ai,在他们之间插入一个1即可。

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef long double LD;
const LL N = 2e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
int a[N];
void solve()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
		scanf("%d", &a[i]);
	vector<int>ans{ a[1] };
	for (int i = 2; i <= n; ++i)
	{
		if (a[i - 1] > a[i])
			ans.push_back(1);
		ans.push_back(a[i]);
	}
	printf("%d\n", ans.size());
	for (auto i : ans)
		printf("%d ", i);
	printf("\n");
}
int main()
{
	int T = 1;
	scanf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

C. Flower City Fence

题意:

给出一个由n个长方形拼成的图形,在第i个的长方形高度为ai,宽为1(ai-1>=ai)。问把它换一个方向摆之后是否和原图像一样。(看看原题的图应该很好看懂题意)

题解:

可以先求出换防线摆之后的图像在每一行的高度然后暴力判断是否相等。

若第i行的高度为ai,则换方向之后第1到ai行的高度至少为i。给每个bi赋值之后然后从右往左bi=max(bi,bi+1)即可。特别的若存在ai>n则直接无法匹配输出NO(ai太大的情况数组存不下)。

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef long double LD;
const LL N = 2e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
int a[N], b[N];
void solve()
{
	int n;
	scanf("%d", &n);
	memset(b, 0, sizeof(int) * (n + 2));
	for (int i = 1; i <= n; ++i)
		scanf("%d", &a[i]);
	for (int i = 1; i <= n; ++i)
	{
		if (a[i] > n)
		{
			printf("NO\n");
			return;
		}
		b[a[i]] = i;
	}
	for (int i = n; i >= 1; --i)
	{
		b[i] = max(b[i], b[i + 1]);
		if (a[i] != b[i])
		{
			printf("NO\n");
			return;
		}
	}
	printf("YES\n");
}
int main()
{
	int T = 1;
	scanf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

D. Ice Cream Balls

题意:

求一个集合,使得在集合中任意选择两个元素产生的子集合方案数刚好为n。({1,2},{2,1}被认为是同一种方案),输出这个集合的最少元素个数。

题解:

考虑每个元素都不同的情况,设元素个数为s个,则方案数为s*(s-1)/2。

在每个数都不同的基础上,若我们往集合中加入x个互不相同并且属于当前集合的数,我们的方案数会增加x个。

所以我们可以先二分不同数的个数s,若当每个数都不同的情况产生的方案数刚好为n,则输出此方案s。若不能做到,则先找到一个使得s*(s-1)/2小于n的方案,然后再往其中加入集合中已存在的数,最终答案为s+n-s*(s-1)/2。

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef long double LD;
const LL N = 2e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
void solve()
{
	LL n, l = 2, r = 2e9 + 10;
	scanf("%lld", &n);
	while (l < r)
	{
		LL mid = l + r >> 1;
		if (mid * (mid - 1) / 2 < n)
			l = mid + 1;
		else
			r = mid;
	}
	if (l * (l - 1) / 2 == n)
		printf("%lld\n", l);
	else
	{
		LL t = l - 1, s = t * (t - 1) / 2;
		printf("%lld\n", t + n - s);
	}
}
int main()
{
	int T = 1;
	scanf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

E. Kolya and Movie Theatre

题意:

给出n场次电影,每看一场电影会使你获得ai点满意度,你最多可以看m场电影。由于你很急急急急急,假设你上一场看的电影场次为x,当你看了一场场次为y的电影时会减去(y-x+1)*d点满意度(当没有前一场时x为1)。求你可能获得的最大满意度。

题解:

易知当我们看到最后一场电影为x时,总计要减去的满意度为x*d。所以我们只要枚举最后一场电影的场次,然后用multiset或者小顶堆维护前m大的满意度的电影集合和看电影获得的满意度总和即可。(一种方法见代码)

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<set>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef long double LD;
const LL N = 2e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
LL a[N];
multiset<int>st;
void solve()
{
	LL n, m, d, ans = 0, sum = 0;
	scanf("%lld%lld%lld", &n, &m, &d);
	st.clear();
	for (int i = 1; i <= n; ++i)
	{
		scanf("%lld", &a[i]);
		if (a[i] > 0 && st.size() < m)
		{
			st.insert(a[i]);
			sum += a[i];
		}
		else if (*st.begin() < a[i])
		{
			sum -= *st.begin();
			st.erase(st.begin());
			sum += a[i];
			st.insert(a[i]);
		}
		ans = max(ans, sum - i * d);
	}
	printf("%lld\n", ans);
}
int main()
{
	int T = 1;
	scanf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

F. Magic Will Save the World

题意:

Vika能够使用两种魔法:火魔法与水魔法。Vika的初始火水法力值都为0,Vika每秒能获得w点火魔力值与f点水魔力值。

有n只怪需要Vika去击杀,每只怪有si点生命,Vika可以使用si点火法力值或者si点水法力值击杀掉第i只怪物(击杀同一只怪物不能混用水与火)。Vika击杀怪物不需要花费时间,也就是说Vika可以先攒够足够击杀所有的怪物的法力值然后瞬间击杀所有怪物。

求Vika击杀所有怪物的最少时间。

题解:

题目给出的n(n<=100)与si(si<=1e4)很小,我们可以计算出所有可能加出来的\sum si,设sum=\sum_{i=1}^{n}si,我们可以枚举所有可能加出来的\sum si,然后求出用水魔法击杀生命总和为\sum si的一堆怪,用火魔法击杀生命总和为sum-\sum si 的一对怪的最少时间t。最终答案为min({t})

由于n<=100,si<=1e4,所以\sum si的最大值为1e6,枚举所有可能加出来的数的时间复杂度为n*sum最大1e8,题目给出的时间为4s而且1e8是跑不满的完全可以过。我用bitset优化了一下,时间复杂度为O(n*sum/w+sum)。(大概1e6的数量级)

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<bitset>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef long double LD;
const LL N = 1e6 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
bitset<N>st;
void solve()
{
	LL w, f, ans = 1e18;
	int n, sum = 0;
	scanf("%lld%lld%d", &w, &f, &n);
	st.reset();
	st[0] = 1;
	for (int i = 1, a; i <= n; ++i)
	{
		scanf("%d", &a);
		st |= st << a;
		sum += a;
	}
	for (int i = 0; i <= sum; ++i)if (st[i])
	{
		int t = sum - i;
		ans = min(ans, max((i + w - 1) / w, (t + f - 1) / f));
	}
	printf("%lld\n", ans);
}
int main()
{
	int T = 1;
	scanf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

G. The Great Equalizer

题意:

给出一个n个元素的数组a。

给出一种操作f(a),依次进行以下操作:1、对a排序并去重。2、设当前a数组长度为m,对于所有1<=i<=m,使ai+=m-i+1。3、去重,若去重之后数组a中仅剩一个元素则输出它,若存在多个元素则在此回到2操作。如此循环。

给出q个询问,每组询问给出一对i,x,先将ai修改为x之后输出当前数组的f(a)。(后面的修改操作会继承前面已经执行的操作)

题解:

我们将a数组进行排序去重之后,我们会发现每次进行一轮操作之后会使得相邻元素ai与ai+1的差值减少1,设排序之后的数组中相邻元素的最大差值为maxd,则我们需要进行maxd次操作使得数组仅剩一个元素。则易知f(a)=max({ai})+maxd。(最大值进行maxd次操作之后会增加maxd个1)

所以对于一个数组a我们只需要知道他们最大的相邻元素的差值以及数组a的最大值我们就可以知道经过操作之后仅剩的元素f(a)。而该问题有q次对数组的修改与询问,我们需要动态的维护所有数的集合以及所有差值的集合,可以通过map或者multiset来完成集合a与差值集合的维护(写起来挺烦的,像是在写模拟题,具体做法见代码)

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef long double LD;
const LL N = 2e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
int a[N];//维护原数组a
map<int, int>mp;//维护去重排序之后的原数组
multiset<int>st;//维护差值
void solve()
{
	mp.clear();//初始化
	st.clear();
	st.insert(0);
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
	{
		scanf("%d", &a[i]);
		mp[a[i]]++;
	}
	for (auto it = mp.begin(); it != mp.end(); ++it)
	{
		auto rit = ++it;
		--it;
		if (rit == mp.end())break;
		st.insert(rit->first - it->first);
	}
	int q;
	vector<int>ans;
	scanf("%d", &q);
	while (q--)
	{
		int idx, x;
		scanf("%d%d", &idx, &x);
		//删除原ai
		auto it = mp.find(a[idx]);
		--it->second;
        //设去重排序之后ai在数组的下标为t
		if (it->second == 0)//去重并排序的集合中的ai被删除了
		{
			if (it != mp.begin())//删除a[t]-a[t-1],若a[t-1]存在
			{
				auto lit = --it;
				++it;
				int d = it->first - lit->first;
				st.erase(st.find(d));
			}
			auto rit = ++it;
			--it;
			if (rit != mp.end())//删除a[t+1]-a[t],若a[t+1]存在
			{
				int d = rit->first - it->first;
				st.erase(st.find(d));
			}
			if (it != mp.begin() && rit != mp.end())//加入a[t+1]-a[t-1],若他俩都存在
			{
				auto lit = --it;
				++it;
				int d = rit->first - lit->first;
				st.insert(d);
			}
			mp.erase(it);
		}
		//加入新的ai
		a[idx] = x;
		mp[x]++;
		it = mp.find(x);
        //设去重排序之后x在数组的下标为t
		if (it->second == 1)//去重并排序的集合中新增了x
		{
			if (it != mp.begin())//加入a[t]-a[t-1],若a[t-1]存在
			{
				auto lit = --it;
				++it;
				int d = it->first - lit->first;
				st.insert(d);
			}
			auto rit = ++it;
			--it;
			if (rit != mp.end())//加入a[t+1]-a[t],若a[t+1]存在
			{
				int d = rit->first - it->first;
				st.insert(d);
			}
			if (it != mp.begin() && rit != mp.end())//删除a[t+1]-a[t-1],若他俩都存在
			{
				auto lit = --it;
				++it;
				int d = rit->first - lit->first;
				st.erase(st.find(d));
			}
		}
//		for (auto i : st)
//			printf("%d ", i);
//		printf("%d\n", mp.rbegin()->first + *st.rbegin());
		ans.push_back(mp.rbegin()->first + *st.rbegin());
	}
	for (auto i : ans)
		printf("%d ", i);
	printf("\n");
}
int main()
{
	int T = 1;
	scanf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

(其实也可以用俩multiset而不用map,反正要的是最大的差值不去重也无所谓,去重之后反而会有当集合中本来就只有一个元素时需要在差值集合中提前插入一个0的问题)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值