Sky Inc, Programming Contest 2023(AtCoder Beginner Contest 329) - AtCoder
纯纯手速场,很典的F,使我上大分
A - Spread
题意:
给出一个字符串,让你用空格分割每一个字符
题解:
纯纯凑数题
char ch[N];
void solve()
{
scanf("%s", ch + 1);
for (int i = 1; ch[i]; ++i)
printf("%c ", ch[i]);
}
B - Next
题意:
求数组A中不是最大值的数中的最大值(人话:去重之后的次大值)
题解:
怎么写怎么过,我直接set瞎搞了,总之就是去重之后找最大值就行
set<int>st;
void solve()
{
int n;
scanf("%d", &n);
for (int i = 1, a; i <= n; ++i)
scanf("%d", &a), st.insert(a);
st.erase(*st.rbegin());
printf("%d\n", *st.rbegin());
}
C - Count xxx
题意:
给出一个字符串,求满足由仅一种字符构成的不同子串的数量
题解:
对于由字符c构成的子串中,我们只需要考虑最长的由c构成的子串即可,因此统计所有字符的最长连续子串长度的最大值,它们的和即最终答案
char ch[N];
void solve()
{
int n, mx[26] = { 0 };
scanf("%d%s", &n, ch + 1);
for (int i = 2, t = 1; i <= n + 1; ++i)
{
if (ch[i] == ch[i - 1])
++t;
else
{
int c = ch[i - 1] - 'a';
mx[c] = max(mx[c], t);
t = 1;
}
}
int ans = 0;
for (int i = 0; i < 26; ++i)
ans += mx[i];
printf("%d\n", ans);
}
D - Election Quick Report
题意:
有N人参与选举,M人轮流投票,每当一个人投入他的选票之后,求出获得选票最多的那个人的编号(如选票一样多则输出编号最小的那个)
题解:
用set<pair<int,int>>维护<选票数量,编号>按选票数降序,编号升序,并用数组维护每个人的选票数cnt[i],当给x号投票时,删除{cnt[x],x},加入{++cnt[x],x}即可
我们可以把维护的选票数变成维护选票数的相反数,这样就不用重载小于号了
int cnt[N];
set<PII>st;
void solve()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
st.insert({ 0,i });
while (m--)
{
int x;
scanf("%d", &x);
st.erase({ cnt[x],x });
st.insert({ --cnt[x],x });
printf("%d\n", st.begin()->second);
}
}
E - Stamp
题意:
给出一个长度为N的字符串S,一个长度为M的字符串T。
刚开始你有一个内容全为'#'的字符串X,你可以进行以下操作任意次:
选择一个位置i(1<=i<=n-m+1),将字符串T覆盖到位置i到i+m-1(具体操作可以看题目样例1的解释)
判断经过若干次操作之后字符串X能否变成字符串S
题解:
我们可以对题目的操作逆向操作,从字符串S中找到字符串T,然后把他们全部变为'#',同时'#'能代替任何字符(如"ABC" = "AB#" = "#BC"),如果能将字符串S全部变为'#',则X能变为S
考虑dfs操作,当S中的一段被转化为'#'之后,新产生的与T串相等的串一定会在刚删除的串的左右m个位置内,因此我们只需要在删除i位置的串之后再在i位置的左右m格内尝试找到T,如果找到就删除然后再下一个位置继续搜,不断dfs下去即可。为了保证同一个位置不会搜很多次,我们特判"###"!="ABC"。
我测这题解写的好烂
int n, m;
char s[N], t[N];
bool check(int x)
{
bool flag = 0;
for (int i = 1; i <= m; ++i)
{
if (s[x + i - 1] != '#')
{
if (s[x + i - 1] != t[i])
return 0;
flag = 1;
}
}
return flag;
}
void fun(int x)
{
for (int i = 1; i <= m; ++i)
s[x + i - 1] = '#';
}
void dfs(int x)
{
fun(x);
for (int i = max(1, x - m + 1); i < x; ++i)
if (check(i))dfs(i);
for (int i = x + 1; i <= x + m - 1 && i + m - 1 <= n; ++i)
if (check(i))dfs(i);
}
void solve()
{
scanf("%d%d%s%s", &n, &m, s + 1, t + 1);
for (int i = 1; i + m - 1 <= n; ++i)
if (check(i))dfs(i);
for (int i = 1; i <= n; ++i)
{
if (s[i] != '#')
{
printf("No\n");
return;
}
}
printf("Yes\n");
}
F - Colored Ball
题意:
给出N个盒子,刚开始每个盒子里都有一个颜色为Ci的球。
给出Q次操作,每个操作都有一对a, b表示将箱子a中的球全部倒入箱子b,并询问此事箱子b中球的不同的颜色数量。
题解:
好典的启发式合并,刚好昨天div3的G能树上启发式合并今天刚看过启发式合并的思路。
关于启发式合并可以看下面这个,今天刚看过...以下的题解基本只是在复述下面链接里的证明
数据结构学习笔记(8) 启发式合并 - 知乎 (zhihu.com)
我们对每个箱子维护一个set表示该箱子中存在的球的颜色。
对于每次合并我们都将小的集合合并到大的集合,这样我们可以保证每个元素被合并进其他集合时当前集合的大小是至少翻倍的,那对于一个元素来说,这个元素最多被合并logn次。
当我们将集合a合并入集合b时,若集合a的大小大于集合b,我们先swap(st[a],st[b])(这玩意时间复杂度是O(1)的,差不多就相当于是交换了一下指针)然后再将集合a中的所有元素丢入集合b中,然后再将集合a清空。
显然这样操作的结果与不交换是完全等同的,而时间复杂度就如上面的证明,每个元素都最多被合并logn次,时间复杂度nlogn,同时因为使用了set,最终时间复杂度是nlogn^2,题目给了4s嘎嘎过
很短的代码
set<int>st[N];
void solve()
{
int n, q;
scanf("%d%d", &n, &q);
for (int i = 1, c; i <= n; ++i)
scanf("%d", &c), st[i].insert(c);
while (q--)
{
int a, b;
scanf("%d%d", &a, &b);
if (st[a].size() > st[b].size())
swap(st[a], st[b]);
for (auto i : st[a])
st[b].insert(i);
st[a].clear();
printf("%d\n", st[b].size());
}
}