牛客网NOIP赛前集训营-普及组(第一场)

版权声明:转载请联系QQ:1810647785 https://blog.csdn.net/weixin_42619451/article/details/82532622

A 绩点

链接:https://www.nowcoder.com/acm/contest/164/A
来源:牛客网

题目描述

小A刚考完大学考试。现在已经出了 n 门课的成绩,他想自己先算一下这些课的绩点是多少。设第i门课的他拿到的绩点是 g p a i ,而这门课的学分是 s c i ,那么他的总绩点用下面的公式计算:

i = 1 n g p a i × s c i i = 1 n s c i

换言之,设 S s c i 的和, T g p a i s c i 的乘积的和。那么小A的绩点就是 T 除以 S 的值。

输入描述

第一行一个整数 n
接下来 n 行,每行两个数 g p a i s c i

输出描述

输出一行一个实数,表示小A的绩点。输出四舍五入的保留1位小数。

实例1

输入

3
3.7 2
4.0 2
3.7 5

输出

3.8

备注

总共有 5 个数据点:
1 个数据点,满足所有学科得到的 g p a 都相同。
2 个数据点,满足 n = 3
3 个数据点,满足所有学科的 s c 值都相同。
对于所有数据点,都满足 n <= 50 , g p a i 等于 3.3 , 3.7 4.0 s c i 为不超过 5 ,不小于 1 的整数。

题解

这道题定位为简单题。前三个点的部分分可以这样做。
第一个点,所有的 g p a 都相同,因此直接输出任一一门课的 g p a 即可。
第二个点 n = 3 ,只有三门课,暴力输入后手算,为不会数组的同学准备。
第三个数据点,所有科目的学分都是相同的,按照式子计算就与学分无关了,答案就是 g p a 的平均数
正解:利用数组与 f o r 循环,把所有数据读取进来。再按照给定的求和式子计算。且考察浮点数的计算。

实现

#include <bits/stdc++.h>
using namespace std;
int n;
double gpa[1000];
int sc[1000];
int main()
{
    cin >> n;
    for(int i = 0;i < n;i ++) 
        scanf("%lf %d", &gpa[i], &sc[i]);
    double ans = 0, sum = 0;
    for(int i = 0;i < n;i ++) ans += gpa[i] * sc[i], sum += sc[i];
    printf("%.1f\n", ans / sum);
    ret 
urn 0;
}

B 巨大的棋盘

链接:https://www.nowcoder.com/acm/contest/164/B
来源:牛客网

题目描述

小A站在一个巨大的棋盘上。这个棋盘可以看成是一个网格图。这个网格图的大小为 n × m 。左上角坐标为 ( 1 , 1 ) ,右下角坐标为 ( n , m ) 。这个棋盘很特别,他每行每列都是一个环。具体来说,当小A站在第一行,他往上走的时候,他会走到第 n 行,站在第 n 行往下走会走到第一行。对于第一列和第 m 列类似。小A在棋盘上可以上下左右走,假设他站在位置 ( i , j ) ,向上走,会走到 ( i 1 , j ) ,向下回到 ( i + 1 , j ) ,向左到 ( i , j 1 ) ,向右到 ( i , j + 1 ) 。注意由于棋盘是循环的,他不会走出这个棋盘。
现在小A有一个固定的行走序列 S ,代表他每一步走的方向, U 代表向上, D 代表向下, L 代表向左, R 代表向右。比如小A一开始在 ( 1 , 1 ) ,棋盘大小为 3 × 4 。行走序列为 U U L R D 。那么他会依次经过 ( 3 , 1 ) , ( 2 , 1 ) , ( 2 , 4 ) , ( 2 , 1 ) , ( 3 , 1 ) 。但小A觉得只走一遍 S 太无聊,因此他会重复走这个序列 T 次。比如上面的例子,当 T = 2 时,真正的行走序列为 U U L R D U U L R D
小A有 q 个备选的起点位置。他一开始先给定你棋盘大小与行走序列,对于每个起点位置,他想知道,他沿着序列走,最终会走到哪个位置停下。

输入描述

第一行三个整数 n , m , T
接下来一行一个字符串 S ,代表行走序列。注意行走序列在真实走的时候要重复 T 次。
接下来一个整数 q
接下来 q 行,每行两个整数 x , y ,代表小A的一个备选起点。

输出描述

输出 q 行,每行两个整数,输出对于这个起点,最后的终点是哪里。

示例1

输入

3 6 4
DUUUDLLLLR
3
3 2
2 5
1 4

输出

2 2
1 5
3 4

备注

20%: |S| * T <= 10^6, q = 1
40%: |S| * T <= 10^6, q <= 10^5
60%: |S|, T <= 10^5, q <= 10^5
100%: 1 <= T,n,m <= 10^9, 1 <= x <= n, 1 <= y <= m. 1<= q, |S| <= 10^5
其中|S|代表S的长度。

题解

部分分分别是直接模拟, T 次重复没有用乘法而是用加法,以及每次询问重复运算。
正解:解决这道题,需要知道,一个既然棋盘是循环的,那么只关心一个操作序列在 x 坐标与 y
标上走了多少。假设最终走了 L x L y ,可以把他们分别对 n , m 取模。 T 次重复只需要把他们都乘上 T 即可。对于每次询问,直接坐标加上 L x L y

实现

//N * K * S(9,K)
#include <bits/stdc++.h>

using namespace std;

const int maxn = 105;
typedef long long ll;

char s[maxn][1005];
ll has[1005], inc[maxn][15], nw[maxn][15];
int n,l, k, bel[10], ord[maxn], ans;

/*ll Ra()
{
    return rand() * rand() * 1ll * rand() * rand(); //WIN
    return rand() * 1ll * rand(); //LINUX
}*/

bool cmp(int a,int b)
{
    for(int i = 0;i < k;i ++)
        if (nw[a][i] < nw[b][i]) return 1; else
            if (nw[a][i] > nw[b][i]) return 0;
    return 0;
}

int check()
{
    for(int i = 1;i <= n;i ++)
    {
        memset(nw[i], 0, sizeof nw[i]);
        for(int j = 0;j < 8;j ++) nw[i][bel[j]] ^= inc[i][j];
        ord[i] = i;
    }
    sort(ord + 1, ord + n + 1, cmp);
    int ans = 0, siz = 1;
    for(int i = 2;i <= n;i ++)
        if (!cmp(ord[i - 1], ord[i]) && !cmp(ord[i], ord[i - 1])) ++ siz; else
            ans += siz * (siz - 1) / 2, siz = 1;
    ans += siz * (siz - 1) / 2;
    return ans;
}

void dfs(int now,int m)
{
    if (now >= 8)
    {
        if (m != k) return;
        ans = max(ans, check());
        return;
    }
    for(int i = 1;i <= m;i ++)
        bel[now] = i - 1,dfs(now + 1,m);
    bel[now] = m, dfs(now + 1, m + 1);
}

int main()
{
    freopen("string.in","r",stdin),freopen("string.out","w",stdout);
    scanf("%d%d%d", &n, &l, &k);
    k = max(1, 8 - k);
    for(int i = 1;i <= n;i ++) scanf("%s", s[i] + 1);
    for(int i = 1;i <= l;i ++)
        has[i] = Ra();
    for(int i = 1;i <= n;i ++)
        for(int j = 1;j <= l;j ++) inc[i][s[i][j] - 'a'] ^= has[j];
    dfs(0,0);
    printf("%d\n", ans);
    return 0;
}

C 括号

链接:https://www.nowcoder.com/acm/contest/164/C
来源:牛客网

题目描述

小A有一个只包含左右括号的字符串 S 。但他觉得这个字符串不够美观,因为它不是一个合法的括号串。一个合法的括号串是这样定义的:
1. ( ) 是合法的括号串。
2. 若 A 是合法的括号串,则 ( A ) 则是合法的括号串。
3. 若 A , B 是合法的括号串,则 A B 也是合法的括号串。
小A现在希望删掉 S 中若干个字符,使得剩下的字符串是一个合法的括号串。小A想知道有多少不同的方案。两个方案是不同的,当且仅当他们删除的位置不同。比如当 S ( ( ) 时,有两种方案。分别是删掉第一个位置,或是删掉第二个位置。

输入描述

第一行一个整数 n ,代表 S 的长度。
第二行输入 n 个字符,字符要么是 ( ,要么是 ) 。代表串 S

输出描述

一行一个整数,代表不同的方案数。答案对 10 9 + 7 取模。

示例1

输入

8
)(()(())

输出

30

备注

20 % : n <= 20
40 % : n <= 100
60 % : n <= 1000
100 % : n <= 10000

题解

这题的第一档部分分是暴力枚举删掉哪些括号,然后判断是否是合法的括号序列。这个可以使用栈来做,每次遇到一个右括号则弹出左括号,遇到左括号则加入栈中,不合法当且仅当
最后有多余的左括号或中途不够左括号弹出。
正解:可以发现只关心中间每一步有多少左括号。可以使用 D P 。设 F [ i ] [ j ] 表示考虑到第 i 个位置,当前选取的子序列(或删剩的括号序列)结尾恰好为第 i 个位置。左括号数量为 j 。那么可以枚举上一个位置 i , 以及对应的左括号数量 j 。因为 i 位置必须考虑,则若 i 为右括号,则 j = j 1 ,否则 j = j + 1 。但 j >= 0 。最后的答案就是 F [ i ] [ 0 ] 的和。这个 D p 的复杂度是 O ( n 3 ) 的。
但是可以把 i 位置不选也加入转移,那么就是 F [ i ] [ j ] > F [ i + 1 ] [ j ] 。复杂度 O ( n 2 ) 。使用滚动数
组优化空间即可。


D 配对

链接:https://www.nowcoder.com/acm/contest/164/D
来源:牛客网

题目描述

小A有 n 个长度都是 L 的字符串。这些字符串只包含前 8 个小写字符, a   h 。但这些字符串非常的混乱,它们几乎长得互不相同。小A想通过一些规则,让它们长得尽可能相同。小A现在有 K 次机会,他可以每次机会,可以选择一对字符 x , y ,让 x , y 变成等价的字符(注意这里 x , y 和字符 x , y 不是一样的,只是个代号)。注意,等价关系是有传递性的。比如小A让 a b 等价, b c 等价,那么 a c 等价。
对于两个长度字符串 P , Q 是等价的,当且仅当对于每一位, P 的字符和 Q 的字符都是等价的。
小A希望你告诉他,要怎么利用好这 K 次机会(当然可以不用完),使得尽可能多对字符串是等价的。注意每对字符串只能算一次。

输入描述

第一行输入三个整数 n , L , K
接下来 n 行,每行给出一个长度为 L 的字符串。

输出描述

输出一行一个整数,代表最多可能的等价的字符串对。

示例1

输入

5 4 2
ccdd
babd
bdcd
ccda
bacd

输出

4

备注

数据包含 10 个数据点。每个数据点可能有不同的特性。
对于第 1 , 2 个数据点: 保证每个字符串只包含前 4 个小写字母
对于第 3 , 4 个数据点:每个字符串都只包含一种字母。
对于第 5 , 6 个数据点: n <= 10 , L <= 100
对于所有数据,满足: n <= 100 , L <= 1000 , K <= 28 ,每个字符串只包含前8个小写字母。

题解

正解:直接枚举选择哪些字母配对很慢,可以考虑枚举联通块。当 K > 7 时,显然所有串都能相同了,因为只需要 7 条边就能让所有字母等价。因此考虑枚举字母的联通块情况。而贪心地想, K 肯定用完最好,因此联通块情况就只有 S ( 8 , 8 K ) 种情况,其中 S 为第二类斯特林数。枚举完联通块情况后,每个字符串中,每种字符替代为对应联通块编号,变成等价的字符串。然后计算哈希排序算等价对数。但这样可能超时。可以按字母分别维护每个字母出现位置的哈希值,重新算时复杂度是 O ( Σ ) ,而不是 O ( L ) 的。

实现

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 110;
const int maxm = 1010;

char s[maxn][maxm];
ll has[maxm], inc[maxn][15], nw[maxn][15];
int n, l, k, bel[10], ord[maxn], ans;

ll Ra() {
    return rand() * rand() * 1ll * rand() * rand();   //WIN
    return rand() * 1ll * rand();   //LINUX
}

bool cmp(int a, int b) {
    for(int i = 0; i < k; i++) {
        if(nw[a][i] < nw[b][i]) {
            return 1;
        } else if(nw[a][i] > nw[b][i]) {
            return 0;
        }
    }
    return 0;
}

inline int check() {
    for(int i = 1; i <= n; i++) {
        memset(nw[i], 0, sizeof(nw[i]));
        for(int j = 0; j < 8; j++) {
            nw[i][bel[j]] ^= inc[i][j];
        }
        ord[i] = i;
    }
    sort(ord + 1, ord + n + 1, cmp);
    int ans = 0, siz = 1;
    for(int i = 2; i <= n; i++) {
        if(!cmp(ord[i-1], ord[i]) && !cmp(ord[i], ord[i-1])) {
            siz++;
        } else {
            ans += siz * (siz - 1) / 2, siz = 1;
        }
    }
    ans += siz * (siz - 1) / 2;
    return ans;
}

inline void dfs(int now, int m) {
    if(now >= 8) {
        if(m != k) return ;
        ans = max(ans, check());
        return ;
    } else {
        for(int i = 1; i <= m; i++){
            bel[now] = i - 1;
            dfs(now + 1, m);
        }
        bel[now] = m;
        dfs(now + 1, m + 1);
    }
}

int main() {
    scanf("%d%d%d", &n, &l, &k);
    k = max(1, 8 - k);
    for(int i = 1; i <= n; i++) {
        scanf("%s", s[i] + 1);
    }
    for(int i = 1; i <= l; i++) {
        has[i] = Ra();
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= l; j++) {
            inc[i][s[i][j] - 'a'] ^= has[j];
        }
    }
    dfs(0, 0);
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42619451/article/details/82532622