ABC308 A-G题解

CodeQUEEN 2023 予選 (AtCoder Beginner Contest 308) - AtCoder

写的烂,建议直接看官方题解

A - New Scheme

题意:

给定一个8个整数组成的数组S,问这个数组是否满足:1、非降序,2、100<=Si<=675,3、Si是25的倍数

题解:

签到

#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 pair<LL, LL> PLL;
typedef unsigned long long ULL;
const LL N = 2e5 + 10, MOD = 1e9 + 7;
void solve()
{
	int a[10];
	for (int i = 1; i <= 8; ++i)
		scanf("%d", &a[i]);
	for (int i = 1; i < 8; ++i)
	{
		if (a[i] > a[i + 1])
		{
			printf("No\n");
			return;
		}
	}
	for (int i = 1; i <= 8; ++i)
	{
		if (a[i] < 100 || a[i]>675 || a[i] % 25)
		{
			printf("No\n");
			return;
		}
	}
	printf("Yes\n");
	return;
}
int main()
{
	int T = 1;
	//canf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

B - Default Price

题意:

给定N盘料理Ci,给定M种料理Di以及M+1种价格Pi,料理D1到Dm与价格P1与Pm对应,不存在与D中的料理价格为P0,求N盘料理的总价格

题解:

map偷懒

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<string>
#include<map>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
typedef unsigned long long ULL;
const LL N = 2e5 + 10, MOD = 1e9 + 7;
string s[110], s1[110];
int p[110];
map<string, int>mp;
void solve()
{
	int n, m, ans = 0;
	cin >> n >> m;
	for (int i = 1; i <= n; ++i)
		cin >> s[i];
	for (int i = 1; i <= m; ++i)
		cin >> s1[i];
	for (int i = 0; i <= m; ++i)
		cin >> p[i];
	for (int i = 1; i <= m; ++i)
		mp[s1[i]] = p[i];
	for (int i = 1; i <= n; ++i)
	{
		if (mp.find(s[i]) != mp.end())
			ans += mp[s[i]];
		else
			ans += p[0];
	}
	cout << ans << endl;
}
int main()
{
	int T = 1;
	//canf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

C - Standings

题意:

N个人抛硬币,每个人抛出正面的概率为eq?%5Cfrac%7BA%7D%7BA&plus;B%7D,将所有人按抛出正面的概率为第一关键字,编号为第二关键字排序(先按概率排,概率一样按编号排)

题解:

结构体排序,要注意不能用使用浮点数直接表示概率,精度不够,直接用整数(要开LL)

eq?%5Cfrac%7BAi%7D%7BAi&plus;Bi%7D%3C%20%5Cfrac%7BAj%7D%7BAj&plus;Bj%7D%5CRightarrow%20Ai*%5Cleft%20%28%20Aj*Bj%20%5Cright%20%29%3C%20Aj*%5Cleft%20%28%20Ai&plus;Bi%20%5Cright%20%29

#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 pair<LL, LL> PLL;
typedef unsigned long long ULL;
const LL N = 2e5 + 10, MOD = 1e9 + 7;
struct node
{
	int a, b, idx;
};
node s[N];
bool cmp(node x, node y)
{
	LL x1 = x.a + x.b, y1 = y.a + y.b;
	LL x2 = x.a * y1, y2 = y.a * x1;
	if (x2 != y2)
		return x2 > y2;
	return x.idx < y.idx;
}
void solve()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
	{
		scanf("%d%d", &s[i].a, &s[i].b);
		s[i].idx = i;
	}
	sort(s + 1, s + 1 + n, cmp);
	for (int i = 1; i <= n; ++i)
	{
		if (i != 1)printf(" ");
		printf("%d", s[i].idx);
	}
}
int main()
{
	int T = 1;
	//canf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

D - Snuke Maze

题意:

给一个N行M列的由小写字母构成的表格,问是否能从(1,1)开始,按“snuke”循环(sunkesnu...)的顺序到达(N,M)。(只能往周围四格走)

题解:

搜索,dfs和bfs都行

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
typedef unsigned long long ULL;
const LL N = 5e2 + 10, MOD = 1e9 + 7;
char mp[N][N], s[10] = "0snuke";
int n, m, book[N][N], dx[] = {0,0,1,-1}, dy[] = {1,-1,0,0};
bool cheak(int x, int y, int step)
{
	if (x<1 || x>n)return 0;
	if (y<1 || y>m)return 0;
	if (mp[x][y] != s[(step - 1) % 5 + 1])return 0;
	if (book[x][y])return 0;
	return 1;
}
void dfs(int x, int y, int step)
{
	if (!cheak(x, y, step))return;
	book[x][y] = 1;
	for (int i = 0; i < 4; ++i)
	{
		int tx = x + dx[i], ty = y + dy[i];
		dfs(tx, ty, step + 1);
	}
}
void solve()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i)
		scanf("%s", mp[i] + 1);
	dfs(1, 1, 1);
	if (book[n][m])
		printf("Yes\n");
	else
		printf("No\n");
}
int main()
{
	int T = 1;
	//canf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

E - MEX

题意:

给定一个由0,1,2组成的数组A以及一个由M,E,X组成的字符串S,求对于每一组满足i<j<k且Si=M,Sj=E,Sk=X的mex(Ai,Aj,Ak)

题解:

前后缀和。在Si=M的前提下对Ai=0,1,2分别做前缀和m[N][3],再在Si=X的前提下对Ai=0,1,2分别做后缀和x[N][3],枚举所有E,每个Si=E对答案的贡献为

eq?%5Csum_%7Bl%3D0%7D%5E%7Bl%3D2%7D%5Csum_%7Br%3D0%7D%5E%7Br%3D2%7D%20%5Cleft%20%28%20m%5Bi%5D%5Bl%5D*x%5Bi%5D%5Br%5D%20%5Cright%20%29*mex%28l%2Cr%2CAi%29

#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 pair<LL, LL> PLL;
typedef unsigned long long ULL;
const LL N = 2e5 + 10, MOD = 1e9 + 7;
int a[N], m[N][3], x[N][3];
char ch[N];
int mex(int e, int f, int g)
{
	int t[3] = { 0 };
	++t[e], ++t[f], ++t[g];
	if (!t[0])return 0;
	if (!t[1])return 1;
	if (!t[2])return 2;
	return 3;
}
void solve()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
		scanf("%d", &a[i]);
	scanf("%s", ch + 1);
	for (int i = 1; i <= n; ++i)
	{
		for (int j = 0; j < 3; ++j)
			m[i][j] = m[i - 1][j];
		if (ch[i] == 'M')++m[i][a[i]];
	}
	for (int i = n; i >= 1; --i)
	{
		for (int j = 0; j < 3; ++j)
			x[i][j] = x[i + 1][j];
		if (ch[i] == 'X')++x[i][a[i]];
	}
	LL ans = 0;
	for (int i = 2; i < n; ++i)
	{
		if (ch[i] == 'E')
		{
			for (int l = 0; l < 3; ++l)
			{
				for (int r = 0; r < 3; ++r)
					ans += (LL)m[i][l] * x[i][r] * mex(l, r, a[i]);
			}
		}
	}
	printf("%lld", ans);
}
int main()
{
	int T = 1;
	//canf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

F - Vouchers

题意:

你有N件要买的商品,价格分别为Pi,有M张优惠券,每张能使价格在Li以上的商品折扣Di元(直接减),每张优惠券只能用一次,每件商品只能优惠一次。(题目保证Li>=Di)

题解:

贪心优先用掉折扣力度最大的(Di最大的)券,使用券时优先抵满足优惠条件的价格最小的商品(价格更高的商品能满足更多优惠券的使用条件,故先先抵价格最小的商品)。用multiset+lower_bound偷懒

#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 pair<LL, LL> PLL;
typedef unsigned long long ULL;
const LL N = 2e5 + 10, MOD = 1e9 + 7;
struct node
{
	int l, d;
	bool operator<(const node x)const
	{
		return d > x.d;
	}
};
node a[N];
multiset<int>st;
void solve()
{
	int n, m;
	LL ans = 0;
	scanf("%d%d", &n, &m);
	for (int i = 1, x; i <= n; ++i)
	{
		scanf("%d", &x);
		ans += x;
		st.insert(x);
	}
	for (int i = 1; i <= m; ++i)
		scanf("%d", &a[i].l);
	for (int i = 1; i <= m; ++i)
		scanf("%d", &a[i].d);
	sort(a + 1, a + 1 + m);
	for (int i = 1; i <= m; ++i)
	{
		if (st.lower_bound(a[i].l) != st.end())
		{
			ans -= a[i].d;
			st.erase(st.lower_bound(a[i].l));
            //multiset直接erase(x)会删除所有值为x的元素,所以必须删这样删迭代器
		}
	}
	printf("%lld", ans);
}
int main()
{
	int T = 1;
	//canf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

G - Minimum Xor Pair Query

题意:

给出三种操作:1、往集合中加入x,2、从集合中删除x(保证x存在),3、求从集合中任选两个数的最小亦或值(保证此时集合中至少有两个元素)

题解:

可以证明:对于任何x < y < z,min(x ⊕ y, y ⊕ z) < x ⊕ z(证明过程官解有)

解説 - CodeQUEEN 2023 予選 (AtCoder Beginner Contest 308)

因此在一个集合中最小的亦或值必然是两个相邻的数的亦或值

(还有种比较抽象?的想法是既然要异或值最小那两个数从最高位往最低位一定要尽量同样是0或者1,也就是俩数要尽量差的小的情况下异或值更容易小,因此最小异或值是相邻两数的异或值)

所以只要不断维护所有相邻数的亦或值的集合就行了,用一个multiset维护集合,在插入元素时删除将要插入位置前后两个元素的亦或值,并加入这个元素分别与前一个元素以及后一个元素的亦或值,删除同理

题在赛后补的...完全没想到亦或还有这个性质,代码参考的这位的:提出 #43112816 - CodeQUEEN 2023 予選 (AtCoder Beginner Contest 308)

说是参考实际上基本抄的一模一样了...

#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 pair<LL, LL> PLL;
typedef unsigned long long ULL;
const LL N = 3e5 + 10, MOD = 1e9 + 7;
multiset<int>st, ans;
//multiset.erase(x)会删除所有值为x的元素
//因此删除一个值为x的元素需要 st.erase(st.find(x))
void solve()
{
	int op, x;
	scanf("%d", &op);
	if (op == 1)
	{
		scanf("%d", &x);
		auto it = st.upper_bound(x);
		//假设插入后x周围的值是这么个情况:l x r
		if (it != st.begin() && it != st.end())
		{	//删除l^r
			auto lt = it;
			lt--;
			ans.erase(ans.find(*lt ^ *it));
		}
		if (it != st.end())
		{	//插入x^r
			ans.insert(*it ^ x);
		}
		if (it != st.begin())
		{	//插入x^l
			auto lt = it;
			lt--;
			ans.insert(*lt ^ x);
		}
		st.insert(x);
	}
	else if (op == 2)
	{
		scanf("%d", &x);
		auto it = st.find(x);
		//假设删除前x周围的值是这么个情况:l x r
		if (it != st.begin())
		{	//删除x^l
			auto lt = it;
			lt--;
			ans.erase(ans.find(*lt ^ x));
		}
		auto rt = it;
		rt++;
		if (rt != st.end())
		{	//删除x^r
			ans.erase(ans.find(*rt ^ x));
		}
		if (it != st.begin() && rt != st.end())
		{	//插入l^r
			auto lt = it;
			lt--;
			ans.insert(*lt ^ *rt);
		}
		st.erase(st.find(x));
	}
	else
	{
		printf("%d\n", *ans.begin());
	}
}
int main()
{
	int T = 1;
	scanf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值