「Wdoi2021」Windy OI Codeforces Round 1 总结
T1 Beautiful Array
题意:试将一个长度为 n的括号串分为 2m个子序列,子序列可以为空,且每个字符都必须分到恰好一个子序列中。使得至少 m个子序列为匹配的括号串。空序列不算匹配的括号序列。无解请输出 0,否则输出 1。本题多组数据,其中数据组数为 T。定义 A为 B的子序列当且仅当 A 能由 B 在顺序不改变的前提下删除若干元素后得到。\(1\leq T,n,m \leq 200\)
签到题.由于数据非常小,暴力求出可能匹配的括号串与m比较即可.
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 250;
int T, n, m;
string s;
int vis[N];
int main()
{
ios::sync_with_stdio(false);
int T;
cin >> T;
while (T--)
{
cin >> n >> m;
cin >> s;
s = ' ' + s;
for (int i = 1; i <= n; i++)
vis[i] = 0;
int cnt = 0;
for (int i = 1; i <= n; i++)
{
if (vis[i])
continue;
if (s[i] == '(')
for (int j = i + 1; j <= n; j++)
if (s[j] == ')' && !vis[j])
{
vis[i] = 1, vis[j] = 1, cnt++;
break;
}
if (s[i] == ')')
for (int j = 1; j <= i - 1; j++)
if (s[j] == '(' && !vis[j])
{
vis[i] = 1, vis[j] = 1, cnt++;
break;
}
}
if (cnt >= m)
cout << 1 << endl;
else
cout << 0 << endl;
}
}
T2 Alice Wins! (easy version)
AB 每队 2n人正在玩石头剪刀布。A 队第 \(i\) 个人出$ a_i$,B 队第 \(i\) 人出 \(b_i\).编号相同的人会对战。若 A 队赢则加一分,平不得分,输扣一分。你可以至多改变每队 \(n\) 个人的出拳方案,使得 A 队的得分最高。输出得分的最大值和任意一组构造方案。
本题中,我们用 1代表石头,2代表剪刀,3代表布。
签到题.容易发现最坏情况也只需要一边各n次就能改成全赢,所以扫一遍直接改成全赢即可.注意不要让某一边改的次数超过n.
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 500005;
int n;
int cmp(int a, int b)
{
if (a == b)
return 0;
if (a != 1 && b != 1)
{
if (a < b)
return 1;
if (a > b)
return -1;
}
if (a == 1)
{
if (b == 2)
return 1;
if (b == 3)
return -1;
}
if (b == 1)
{
if (a == 2)
return -1;
if (a == 3)
return 1;
}
}
int a[N], b[N];
int cnt;
int main()
{
ios::sync_with_stdio(false);
int T;
cin >> T;
while (T--)
{
cin >> n;
for (int i = 1; i <= 2 * n; i++)
cin >> a[i];
for (int i = 1; i <= 2 * n; i++)
cin >> b[i];
cnt = 0;
for (int i = 1; i <= 2 * n; i++)
if (cmp(a[i], b[i]) == -1 && cnt < n)
{
cnt++, a[i]++;
if (a[i] == 4)
a[i] = 1;
}
for (int i = 1; i <= 2 * n; i++)
if (cmp(a[i], b[i]) == 0 && cnt < n)
{
cnt++, a[i]--;
if (a[i] == 0)
a[i] = 3;
}
cnt = 0;
for (int i = 1; i <= 2 * n; i++)
if (cmp(a[i], b[i]) == -1 && cnt < n)
{
cnt++, b[i]--;
if (b[i] == 0)
b[i] = 3;
}
for (int i = 1; i <= 2 * n; i++)
if (cmp(a[i], b[i]) == 0 && cnt < n)
{
cnt++, b[i]++;
if (b[i] == 4)
b[i] = 1;
}
int sum = 0;
for (int i = 1; i <= 2 * n; i++)
sum += cmp(a[i], b[i]);
cout << sum << endl;
for (int i = 1; i <= 2 * n; i++)
cout << a[i] << " ";
cout << endl;
for (int i = 1; i <= 2 * n; i++)
cout << b[i] << " ";
cout << endl;
}
}
T3 Alice Wins! (hard version)
题意:同T2,但改为每边改恰好n次.
略加思考容易发现,我们只需要解决改完全赢之后如何浪费次数就可以了.显然对于没改过的,即本来就赢的位置,可以上下同时+1/-1浪费两次操作,这样可能会因为奇偶剩下两边中某一边的一次修改,只需要将之前改成赢的的位置从改一边变成赢变成改两边变成赢即可.
码量较大细节较多,但没有太大难度.
#include <iostream>
#include <cstdio>
#include <assert.h>
using namespace std;
const int N = 500005;
int n;
int cmp(int a, int b)
{
if (a == b)
return 0;
if (a != 1 && b != 1)
{
if (a < b)
return 1;
if (a > b)
return -1;
}
if (a == 1)
{
if (b == 2)
return 1;
if (b == 3)
return -1;
}
if (b == 1)
{
if (a == 2)
return -1;
if (a == 3)
return 1;
}
}
int a[N], b[N], arta[N], artb[N], ta[N], tb[N];
int cnta, cntb, cntfail, cntdraw;
int main()
{
ios::sync_with_stdio(false);
int T;
cin >> T;
while (T--)
{
cin >> n;
cntdraw = 0, cntfail = 0, cnta = 0, cntb = 0;
for (int i = 1; i <= 2 * n; i++)
arta[i] = 0, artb[i] = 0;
for (int i = 1; i <= 2 * n; i++)
{
cin >> a[i];
ta[i] = a[i];
}
for (int i = 1; i <= 2 * n; i++)
{
cin >> b[i];
tb[i] = b[i];
}
for (int i = 1; i <= 2 * n; i++)
{
if (cmp(a[i], b[i]) == -1)
cntfail++;
if (cmp(a[i], b[i]) == 0)
cntdraw++;
}
if (cntfail % 2 == 1)
cntfail++;
for (int i = 1; i <= 2 * n; i++)
if (cmp(a[i], b[i]) == -1 && cnta < cntfail / 2)
{
cnta++;
a[i]++;
if (a[i] == 4)
a[i] = 1;
arta[i] = 1;
}
for (int i = 1; i <= 2 * n; i++)
if (cmp(a[i], b[i]) == -1 && cntb < cntfail / 2)
{
cntb++;
b[i]--;
if (b[i] == 0)
b[i] = 3;
artb[i] = 1;
}
int sa = cnta, sb = cntb;
if (cntdraw % 2 == 1)
cntdraw++;
for (int i = 1; i <= 2 * n; i++)
if (cmp(a[i], b[i]) == 0 && cntb - sb < cntdraw / 2)
{
cntb++;
b[i]++;
if (b[i] == 4)
b[i] = 1;
artb[i] = 1;
}
for (int i = 1; i <= 2 * n; i++)
if (cmp(a[i], b[i]) == 0 && cnta - sa < cntdraw / 2)
{
cnta++;
a[i]--;
if (a[i] == 0)
a[i] = 3;
arta[i] = 1;
}
for (int i = 1; i <= 2 * n; i++)
if (cmp(a[i], b[i]) == 1 && !(arta[i] || artb[i]) && cnta < n && cntb < n)
{
cnta++, cntb++;
a[i]++, b[i]++;
if (a[i] == 4)
a[i] = 1;
if (b[i] == 4)
b[i] = 1;
}
if (cnta != n)
for (int i = 1; i <= 2 * n; i++)
if (cmp(a[i], b[i]) == 1 && artb[i] && cnta < n)
{
cnta++;
a[i]++, b[i]++;
if (a[i] == 4)
a[i] = 1;
if (b[i] == 4)
b[i] = 1;
if (b[i] == tb[i])
{
a[i]++, b[i]++;
if (a[i] == 4)
a[i] = 1;
if (b[i] == 4)
b[i] = 1;
}
break;
}
if (cntb != n)
for (int i = 1; i <= 2 * n; i++)
if (cmp(a[i], b[i]) == 1 && arta[i] && cntb < n)
{
cntb++;
a[i]++, b[i]++;
if (a[i] == 4)
a[i] = 1;
if (b[i] == 4)
b[i] = 1;
if (a[i] == ta[i])
{
a[i]++, b[i]++;
if (a[i] == 4)
a[i] = 1;
if (b[i] == 4)
b[i] = 1;
}
break;
}
assert(cnta == n && cntb == n);
int sum = 0;
for (int i = 1; i <= 2 * n; i++)
sum += cmp(a[i], b[i]);
cout << sum << endl;
for (int i = 1; i <= 2 * n; i++)
cout << a[i] << " ";
cout << endl;
for (int i = 1; i <= 2 * n; i++)
cout << b[i] << " ";
cout << endl;
}
}
T4 Magical Expression
给定一个合法的含 |&?01
的后缀表达式(以字符串形式给出,其为“合法的”代表将所有 ?
替换为运算符后其会成为一个合法的后缀表达式),试求出其有多少个子串满足:
- 将所有的
?
替换成|
或&
后,该串为合法的后缀表达式; - 存在一种将所有的
?
替换成|
或&
的方法使得该式所得结果为 0,同时也存在替换方法使得结果为 1。
请输出这样的子串的数量。两个子串是不同的仅当它们长度不同或位置不同。
比T3还要签到的一道题,但细节也比较多,最终也没能调出来.
总结
本场比赛码量较大,难度不大,但由于码力太差只切掉了三个题,luogu仅排到rk204
,主要问题在于:
- T1没有注意到数据范围是钦定暴力做法,想了较长时间没有必要的优化.
- T2没有一遍AC.
- T3一开始想的是把赢改为平.
- T4这一类的表达式题写的不熟练.
本文来自博客园,作者:Kinuhata,转载请注明原文链接:https://www.cnblogs.com/KinuhataSaiai/p/15520006.html