智算之道-第二场初赛-题解

智算之道大赛 第二场初赛 题解


写在前面:

由于比赛时间有其他事情,故本场初赛在比赛时段只做了后面半个小时
题是做的后面的 第三场热身赛(第二场补题)
只追求AC,不追求速度(排名是同题比运时),所有没有细节方面的优化


A - 声控灯

题目描述

小明正在上楼梯,当小明逐渐接近某层楼的时候,这层楼的声控灯检测到小明的脚步声便会亮起来,当小明逐渐远去的时候,声控灯由于一段时间内没检测到声音又会灭下去。
由于小明是匀速上楼的,而且他的速度把控得刚刚好,脚步声的音量也十分恰当,使得灯的亮灭呈现出这样的状态:

  • 他当前所在的楼层的声控灯是亮起的;
  • 他即将抵达的下一层楼的声控灯是亮起的;
  • 他刚刚离开的那层楼的声控灯也是亮起的。


现在你站在楼的外面,通过窗户看到了灯的亮灭状态,请推断小明现在在几楼?

输入格式

每个测试点包含以下内容:
第一行给出一个整数 T T T,表示接下来给出 T T T组测试数据
每组测试数据包含两行
第一行包含两个数 n , m n,m n,m,表示这栋楼一共有 n n n,当前有 m m m盏灯是亮起的,而其余灯都是熄灭的
第二行包含 m m m个整数,这些整数按照从小到大的顺序给出亮起的灯的序号
保证输入数据是符合实际情况的

输出格式

输出 T T T行,每行包含一个数字,按照输入的顺序依次给出每组测试数据的答案。
如果答案不确定,请输出 − 1 -1 1

数据规模与约定

对于 50 % 50\% 50% 的数据, 1 ≤ T ≤ 10 , 3 ≤ n ≤ 100 , m = 3 1 \le T \le 10, 3 \le n \le 100, m = 3 1T10,3n100,m=3
对于 100 % 100\% 100% 的测试点, 1 ≤ T ≤ 1000 , 1 ≤ n ≤ 1 0 9 , 1 ≤ m ≤ 3 1 \le T \le 1000, 1 \le n \le 10^9, 1 \le m \le 3 1T1000,1n109,1m3

解题思路及分析

if出m的所有情况

  1. m == 1 只有一层楼,输入x 输出x
  2. m == 2 && n == 2 无法判断
  3. m == 2 && n != 2 a == 1为底层,b == n为顶层
  4. m == 3 输出中间一层

AC代码

#include 
#include 
using namespace std;
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        int m, n;
        scanf("%d%d", &n, &m);
        if (m == 1)
        {
            int x;
            scanf("%d", &x);
            printf("%d\n", x);
        }
        else if (m == 2)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            if (n == 2)
            {
                printf("-1\n");
            }
            else if (a == 1) 
            {
                printf("%d\n", a);
            }
            else if (b == n)
            {
                printf("%d\n", b);
            }
        }
        else if (m == 3)
        {
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            printf("%d\n", b);
        }
    }
    return 0;
}

B - 构造字符串

题目描述

给定若干个小写英文字母,请你用这些英文字母组成 n n n个字符串,字符串可以是空串。要求这 n n n个字符串的最长公共前缀最长。求出这个最长的长度。
所有字母都要用完,且每个字母只能用在一个字符串里。

名词解释

前缀是指一个字符串从第一个字符开始的连续若干个字符组成的字符串。前缀可以为空串。比如字符串 "aab" 的前缀有四个,分别是 "", "a", "aa", "aab"
我们说一个字符串 s s s是某些串的公共前缀,当且仅当这个串是每个字符串的前缀
每一个公共前缀都是一个字符串,是字符串就有长度。当 n n n个串都确定下来的时候,这 n n n个串的所有公共前缀也就确定下来了,而且可以证明公共前缀的数目是有限的,在这有限个公共前缀中,有一个公共前缀的长度是最长的,这个前缀就是最长公共前缀

输入格式

输入共有两行
第一行是一个整数 n n n,表示字符串的数量
第二行包含 26 26 26个非负整数。从前往后依次表示你拥有字母 a a a的数目,字母 b b b的数目,…,字母 z z z的数目。

输出格式

输出一个非负整数,表示答案

数据规模与约定

对于 30 % 30\% 30% 的数据,除了字母 a a a之外其它字母的数目都为 0 0 0
对于 100 % 100\% 100% 的数据, 2 ≤ n ≤ 1 0 9 2 \le n \le 10^9 2n109 ,每种字母的个数也不超过 1 0 9 10^9 109

解题思路及分析

把每个字符平均分配到各个字符串中,多出来的部分做后缀
每个字符串中分配的字符个数即为结果

AC代码

#include 
using namespace std;
int main()
{
    int n;
    int cnt[26];
    long long ans = 0;
    scanf("%d", &n);
    for (int i = 0; i < 26; i++)
    {
        scanf("%d", &cnt[i]);
        ans += cnt[i] / n;
    }
    printf("%lld\n", ans);
    return 0;
}

C - 情报站

题目描述

现在我方已经查明,敌人通信所使用的加密方式依赖于一个长度为 n n n的数列,只要得知了这个数列中每个数的值,我方便可破解敌方的通信。
通过深入敌人内部的内线人员的艰苦奋斗,我方逐渐获得了一些有用的情报,通过这些情报,整个数列正在被不断地破解。
先后有 m m m条情报被得知,每条情报是以下两种情况之一:

  • 情况 1 1 1:知道了数列中第 x x x个数的值
  • 情况 2 2 2:知道了数列中第 x x x个数和第 y y y个数的和


每得知一条情报,我方都试图破解数列中元素的值
作为情报部门核心技术人员的你,请编程实现如下功能:每次得知一条新情报,你都要计算当前已经能够确定出数列中的多少个数了
你比较笨,对于情况 2 2 2这种情报,只能在已知其中一个数的情况下推出另一个数,不能通过若干情况 2 2 2的情报列方程求解

输入格式

第一行,两个正整数 n , m n,m n,m
接下来 m m m行,每行的第一个数是 t y p e type type

  • 如果 t y p e = 1 type=1 type=1,则接下来跟着一个整数 x x x,表示得知了数列中第 x x x个数的值;
  • 如果 t y p e = 2 type=2 type=2,则接下来跟着两个空格隔开的整数 x , y x,y x,y,表示得知了第 x x x个数和第 y y y个数的和

输出格式

输出 m m m行,每行包含一个非负整数,第 i i i行的非负整数表示在得知了前 i i i条情报之后数列中已经能够确定的数的数量

数据规模与约定

对于 20 % 20\% 20%的数据, 1 ≤ n , m ≤ 10 1 \le n,m \le 10 1n,m10,且只有第一种情报
对于 50 % 50\% 50% 的数据, 1 ≤ n , m ≤ 5000 1 \le n,m \le 5000 1n,m5000
对于 100 % 100\% 100% 的数据, 1 ≤ n , m ≤ 3 × 1 0 5 1 \le n,m \le 3\times 10^5 1n,m3×105
可能会有重复的情报,也可能出现 x = y x=y x=y的情况

解题思路及分析

最开始只拿到20分,算法设计有问题
后面看群里说是并查集,自己大概写了一下,又是WA,后面还有TLE
自己又去补了补并查集的知识,改了半天……
并查集自己用的没有那么熟练,只会写板子,也算是巩固了下

如果遇到type1,x所属的集合的所有元素都能得到结果,上次的答案加上该集合的大小即是这次的答案
如果遇到type2, 就把x和y所属的集合并起来,如果有一个集合答案已知,答案则加上另一集合的大小

AC代码

#include 
using namespace std;

bool vis[300005];
int fa[300005];
int size[300005];

void init(int n)
{
    for (int i = 1; i <= n; i++)
    {
        fa[i] = i;
        size[i] = 1;
    }
}

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

int main()
{
    int n, m, ans = 0;
    scanf("%d%d", &n, &m);
    init(n);
    while (m--)
    {
        int type, x, y;
        scanf("%d", &type);
        if (type == 1)
        {
            scanf("%d", &x);
            int fx = find(x);
            if (!vis[fx])
            {
                vis[fx] = true;
                ans += size[fx];
            }
        }
        else if (type == 2)
        {
            scanf("%d%d", &x, &y);
            if (x == y && !vis[x])
            {
                int fx = find(x);
                if (!vis[fx])
                {
                    vis[fx] = true;
                    ans += size[fx];
                }
            }
            else 
            {
                int fx = find(x), fy = find(y);
                if (fx != fy)
                {
                    if (vis[fx] && !vis[fy])
                    {
                        ans += size[fy];
                        vis[fy] = true;
                    }
                    else if (!vis[fx] && vis[fy])
                    {
                        ans += size[fx];
                        vis[fx] = true;
                    }
                    fa[fx] = fy;
                    size[fy] += size[fx];
                }
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

你可能感兴趣的:(题解)