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个人抛硬币,每个人抛出正面的概率为,将所有人按抛出正面的概率为第一关键字,编号为第二关键字排序(先按概率排,概率一样按编号排)
题解:
结构体排序,要注意不能用使用浮点数直接表示概率,精度不够,直接用整数(要开LL)
#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对答案的贡献为
#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;
}