首页 > 其他分享 >寒假第二周训练部分题解代码

寒假第二周训练部分题解代码

时间:2023-01-15 21:55:48浏览次数:35  
标签:int 题解 cin 背单词 ++ 寒假 include find 第二周

1 周一

1.1 C

输入两个数a, b表示a和b信仰同一个宗教。求有多少种不同的宗教信仰。

先看第二个样例

10 4
2 3
4 5
4 8
5 8

画图可得:![[Pasted image 20230115154805.png]]
一共7个蓝色圈, 7种宗教信仰。

可以定义一个并查集, 初始时每个集合只有自己, 输入a,b就将a,b合并为一个集合。统计已做的有效合并次数, 4-5, 4-8 是有效合并, 而5-8并不是, 本身就在一个并查集里。最后用总个数减去有效合并次数就是集合数量。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
const int N = 5e4 + 10;
int f[N];

int find(int x)
{
    if (f[x] != x)
        f[x] = find(f[x]);
    return f[x];
}

int main()
{

    int n, m;
    int kast = 0;
    while (cin >> n >> m, n || m)
    {
        int cnt = 0;
        for (int i = 1; i <= n; i++)
            f[i] = i;
        while (m--)
        {
            int a, b;
            cin >> a >> b;
            a = find(a), b = find(b);
            if (a != b)
            {
                f[b] = a;
                cnt++;
            }
        }
        cout << "Case " << ++kast << ": " << n - cnt << endl;
    }

    return 0;
}

2 周二

2.1 C

对第一组样例画个图:![[Pasted image 20230115164110.png]]
根据题目性质, 只要社团内一人感染, 整个社团都会被感染。 创建一个并查集, 将同一社团的人合并, 若存在一个人加入了两个社团, 则会将这两个社团一起合并。

然后遍历一次所有学生, 若当前点的父亲与0号点的父亲相同, 则说明会与0号接触, 属于密接, 统计加一即可。

记得访问父亲时使用 find(x), 避免因使用f[x]导致未经路径压缩得到错误结果。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
const int N = 3e4 + 10;
int f[N];

int find(int x)
{
    return f[x] != x ? f[x] = find(f[x]) : f[x];
}

void merge(int a, int b)
{
    a = find(a), b = find(b);
    if (a != b)
        f[a] = b;
}

int main()
{
    int n, m;
    while (cin >> n >> m, n || m)
    {
        for (int i = 0; i < n; i++)
            f[i] = i;

        while (m--)
        {
            int cnt, a;
            cin >> cnt >> a;
            for (int i = 0; i < cnt - 1; i++)
            {
                int b;
                cin >> b;
                merge(a, b);
            }
        }

        int suspect = find(0), res = 0;
        for (int i = 0; i < n; i++)
            res += find(i) == suspect;
        cout << res << endl;
    }
    return 0;
}

3 周三

3.1 C

题意概括一下为, 求从起点开始长度为\(1,3,5,..,\frac{N+1}{2}\) 的乱序序列的中位数。

可以想象为起始是一个空序列, 然后每读入一个数就用插入排序放进序列中, 当序列长度为奇数且小于\(\frac{N+1}{2}\)时输出当前中位数。

中位数即排完序后在中间的数。
创建一个大根堆, 一个小根堆。 大根堆维护前半段的最大值, 小根堆维护后半段的最小值, 这样在长度为奇数时, 存数最多的堆的堆顶元素就是要求的中位数。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <queue>
using namespace std;
const int N = 1e5 + 10;
int a[N];
int n, m;
int mid;
int main()
{
    cin >> n >> mid;
    priority_queue<int, vector<int>, less<int>> q1;    // 大根堆
    priority_queue<int, vector<int>, greater<int>> q2; // 小根堆
    cout << mid << endl;
    for (int i = 2; i <= n; i++)
    {
        int x;
        cin >> x;
        if (x > mid)
            q2.push(x);
        else
            q1.push(x);

        if (i & 1)
        {
            while (q1.size() != q2.size())
            {
                if (q1.size() < q2.size())
                {
                    q1.push(mid);
                    mid = q2.top();
                    q2.pop();
                }
                else
                {
                    q2.push(mid);
                    mid = q1.top();
                    q1.pop();
                }
            }
            cout << mid << endl;
        }
    }
    return 0;
}

4 周四

4.1 B

题意概括一下为, 从一系列单词中找出最短连续的一段, 其中包含最多个“要背单词”, 输出包含数量和该段的长度。

需要一个实现判断当前单词是否为“要背单词”, 可以使用map/set哈希表, 将起初的“要背单词”存下来, 然后用 map.count(s) != 0 来判断是不是“要背单词”。

接着需要找出连续的一段, 其中包含最多个“要背单词”, 且长度最短。
朴素写法是用两层循环枚举所有长度\(O(n^{2})\) , 这里显然会超时, 得优化一下。题目说一段中重复的单词只会计算一次, 为了保持长度最小, 我们需要去重。

定义 res1 为区间内“要背单词”个数, res2 为区间长度。
先枚举区间终点 r, 若当前单词是“要背单词”且第一次出现, 则更新res1res2。接着开始缩短区间并判重。让l往后枚举, 若当前单词不是“要背单词”则直接l++即可, 若是, 接着判断是否在区间内出现过两次及以上, 如果是就继续l++。直到遇见只出现过一次的”要背单词“时停止缩短。此时再更新一次答案即可。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <map>
#include <set>
using namespace std;
const int N = 1e5 + 10;
map<string, int> cnt;
set<string> S;
string s[N];

int main()
{
    int n, m, res1 = 0, res2 = 0;
    cin >> n;
    while (n--)
    {
        string s;
        cin >> s;
        S.insert(s);
    }

    cin >> m;
    int l = 1;
    for (int r = 1; r <= m; r++)
    {
        cin >> s[r];
        if (S.count(s[r]))
        {
            if (!cnt[s[r]]) //  若是第一次出现
                res1++, res2 = r - l + 1;
            cnt[s[r]]++; // 单词出现次数+1
        }
        for (l; l <= r; l++)
        {
            if (S.count(s[l]))
            {
                if (cnt[s[l]] == 1)
                    break;
                else
                    cnt[s[l]]--;
            }
        }
        res2 = min(res2, r - l + 1);
    }
    cout << res1 << endl
         << res2 << endl;
    return 0;
}

4.2 C

题意:给两个最长为80的字符串a,b。求最长的一个字符串c, 使得c是a的后缀, b的前缀 或者 a的前缀, b的后缀。

也就80字符长度, 长度为n的字符串可以贡献n个子串, 加上逆向2n, 一共两个串为4n种, 即 320 种可能的答案。直接枚举答案然后判断可行性求出最长的那个就行。

我猜没人写的原因是看见前面一段英语就弃了(

答案肯定是从a,b的前后缀中得出, 故可以枚举结果串的长度, 用substr函数切割出来对应的前后缀, 若此时满足a的前缀等于b的后缀, 或者a的后缀等于b的前缀, 那么当前长度就是结果长度。

记得从大到小枚举, 这样才是最长。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;

int main()
{
    string a, b;
    cin >> a >> b;
    int n = min(a.size(), b.size()); // 最大长度
    for (int i = n - 1; i >= 0; i--)
    { // pre为字符串长度为i的前缀, last为字符串长度为i的后缀
        string prea = a.substr(0, i), lasta = a.substr(a.size() - i, i);
        string preb = b.substr(0, i), lastb = b.substr(b.size() - i, i);
        if (prea == lastb || preb == lasta)
        {
            cout << i << endl;
            break;
        }
    }

    return 0;
}

5 周五

5.1 B

该题需要记录三个状态, 当前点是否被搜过st, 已经搜了多少个点res, 搜完所有点的方案ans。
其中ans就是我们要求的, 当dfs中res为点总数时将ans++即可。

代码

const int N = 22;
int n, m;
int st[N][N];
int ans;
int dx[] = {-2, -1, 1, 2, 2, 1, -1, -2};
int dy[] = {1, 2, 2, 1, -1, -2, -2, -1};

void dfs(int x, int y, int res)
{
    if (res == n * m)
    {
        ans++;
        return;
    }
    st[x][y] = true;
    for (int i = 0; i < 8; i++)
    {
        int a = x + dx[i], b = y + dy[i];
        if (a < 0 || a >= n || b < 0 || b >= m)
            continue;
        if (st[a][b])
            continue;
        dfs(a, b, res + 1);
    }
    st[x][y] = false;
}

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        int x, y;
        cin >> n >> m >> x >> y;
        memset(st, false, sizeof st);
        ans = 0;
        dfs(x, y, 1);
        cout << ans << endl;
    }
    return 0;
}

5.1 C

搜索顺序:
flower为还可以遇见花的次数, beer为还可以遇见店的个数, sum为当前酒剩余。

当前次遇见若可以是花, 即flower>0, 则继续递归 sum - 1, flower+1。
若可以是点, 即beer>0, 则继续递归 sum*=2, beer+1。

终点条件就当flower和beer都为0, 且sum也为0时, 将最终方案数res++。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
int res;
int n, m;
void dfs(int flower, int beer, int sum)
{
    if (flower == 0 && beer == 0)
    {
        if (!sum)
            res++;
        return;
    }
    if (!sum)
        return;

    if (flower > 0)
        dfs(flower - 1, beer, sum - 1);
    if (beer > 0)
        dfs(flower, beer - 1, sum * 2);
}

int main()
{
    cin >> m >> n;
    dfs(n, m, 2);
    cout << res << endl;
    return 0;
}

标签:int,题解,cin,背单词,++,寒假,include,find,第二周
From: https://www.cnblogs.com/edwinaze/p/17054200.html

相关文章

  • P8944 题解
    非矩乘做法。理论上常数应该小点,但没跑进最优解第一页。可以当个好写做法看。首先发现变换后的答案分布仅与变换前的答案分布有关。所以来研究一次变换中单点的变化。设......
  • 【题解】P6578 [Ynoi2019] 魔法少女网站
    卡了一晚上终于过了。好家伙,又是想题想一半不会是吧,小垃圾是不是想退役/fad小黑子->小垃圾->垃圾酱->垃圾摇滚/xk但是真的有垃圾摇滚这东西/kk思路操作分块......
  • Atcoder Regular Contest ARC 153 A B C D 题解
    点我看题A-AABCDDEFE一个beautifulnumber是形如这样的:\(S1S1S3S4S5S5S7S8S7\)。如果选定了\(S1\),后面的数有100000种选法,所以先求出答案的\(S1\)。假设现在我们要求出......
  • Atcoder Regular Contest ARC 153 A B C D 题解
    点我看题A-AABCDDEFE一个beautifulnumber是形如这样的:\(S1S1S3S4S5S5S7S8S7\)。如果选定了\(S1\),后面的数有100000种选法,所以先求出答案的\(S1\)。假设现在我们要求出......
  • Atcoder Regular Contest ARC 153 A B C D 题解
    点我看题A-AABCDDEFE一个beautifulnumber是形如这样的:\(S1S1S3S4S5S5S7S8S7\)。如果选定了\(S1\),后面的数有100000种选法,所以先求出答案的\(S1\)。假设现在我们要求出......
  • SMU冬训营第二周蓝桥杯模拟赛
    A.解码题目:小明有一串很长的英文字母,可能包含大写和小写。在这串字母中,有很多连续的是重复的。小明想了一个办法将这串字母表达得更短:将连续的几个相同字母写成字母+......
  • 第二周周报
    第二周总结蓝桥杯模拟赛2总结:​ 本次模拟赛共有8道题,4道是入门题和4道普及题,入门题都做对了,但普及题都没做对。还是得学算法,多做题。模拟赛题解(不全......
  • 寒假训练第二周
    寒假训练第二周第一天蓝桥杯模拟赛2F.平面切分大意:N条直线,给定解析式,输出被分成的平面数思路:若所添加的直线与之前直线平行,平面数+1若与已有直线每产生一个不同位......
  • 【题解】P5397 [Ynoi2018] 天降之物
    码力人的甜品,口嗨者的末路。感觉手牵手那个题才是第四分块正体,这个不如叫最初根号分治。思路根号分治。对于每个值,把它们分成出现大于根号次和小于等于根号次两类。先......
  • 【题解】P5692 [MtOI2019]手牵手走向明天
    春节前做大分块是什么奇妙传统吗……这个题好想是好想,但是写起来就是另外一回事了……思路分块。第四分块加强版。区间查询,根号分治做法寄了。看到合并颜色可以想到......