欢乐结训赛题解

Posted x1uc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了欢乐结训赛题解相关的知识,希望对你有一定的参考价值。

欢乐结训赛题解

A 题目链接

  • 题目大意
给你一个字符串,让你求字符串中最大的字母在字母表中排第几 例如 codeforces 中 s 的是最大的 s在字母表中排 19位
  • 代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5 + 10;
    const int mod = 1e9 + 7;
    void solve()
    
        string s;
        int n;
        cin >> n;
        cin >> s;
        int maxx = 0;
        for (int i = 0; i < s.length(); i++)
        
            maxx = max((int)s[i], maxx);
        
        cout << maxx - \'a\' + 1 << \'\\n\';
    
    int main()
    
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
        int __ = 1;
        cin >> __;
        while (__--)
        
            solve();
        
        return 0;
    
    

B 题目链接

  • 题目大意

    给你一个数组a,对于每个a[i],在a数组中找出除了a[i]自己,最大的数,然后输出该数与a[i]的差值 
    
  • 思路

    求出数组中最大的数(maxx_1)和第二大的数(maxx_2),遍历数组,如果a[i]不是最大的数 就输出a[i]-maxx_1,否则输出a[i]-maxx_2
    
  • 代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5 + 10;
    const int mod = 1e9 + 7;
    void solve()
    
        int n;
        cin >> n;
        vector<int> vec(n);
        int maxx = 0;
        int sec = 0;
        for (int i = 0; i < n; i++)
        
            cin >> vec[i];
            if (vec[i] > maxx)
            
                sec = maxx;
                maxx = max(vec[i], maxx);
            
            else if (vec[i] > sec)
            
                sec = vec[i];
            
        
        for (int i = 0; i < n; i++)
        
            if (vec[i] != maxx)
            
                cout << vec[i] - maxx << \' \';
            
            else
            
                cout << maxx - sec << \' \';
            
        
        cout << \'\\n\';
    
    int main()
    
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
        int __ = 1;
        cin >> __;
        while (__--)
        
            solve();
        
        return 0;
    
    

C题目链接

  • 题目大意

    给定一串数组,问这个数组a[ l .....r ]是否满足如下性质
    0 <= l <= r <= n - 1
    a l = a l + 1 = a l + 2...... = ar
    l = 0 or a l - 1 > a l
    r = n - 1 or a r < a r + 1
    若满足这些性质,输出YES 否则输出 NO
    
  • 思路

    形成一个山谷我们需要先下降然后上升
    我们定义一个flag 
    flag=0 代表 现在是无状态
    flag=1 代表 下降状态,如果接下来有一个上升的话就会形成一个山谷
    需要注意 初始时 flag为1,结束时还需要判断一下flag是否为1 如果为1,山谷数+1 原因见原题目
    具体实现见代码
    
  • 代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5 + 10;
    const int mod = 1e9 + 7;
    void solve()
    
        int n;
        cin >> n;
        vector<int> vec(n);
        for (int i = 0; i < n; i++)
            cin >> vec[i];
        int flag = 1;
        int cnt = 0;
        for (int i = 1; i < n; i++)
        
            if (vec[i] > vec[i - 1])  //假如现在是上升 并且flag状态为1 ,山谷数+1
            
                if (flag == 1)
                
                    flag = 0;
                    cnt++;
                
            
            else if (vec[i] < vec[i - 1]) //如果现在是下降 那么将flag赋值为1
            
                flag = 1;
            
        
        if (flag == 1)
            cnt++;
        if (cnt == 1)
            cout << "YES\\n";
        else
            cout << "NO\\n";
    
    int main()
    
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
        int __ = 1;
        cin >> __;
        while (__--)
        
            solve();
        
        return 0;
    
    

D 题目链接

  • 题目大意

    给定一串二进制数组,把其中一个 1 变成 0 或者 0 变成 1 使得数组逆序对数量最大,问这个最大数量是几
    
  • 思路

    先求出不变的情况下 逆序对的个数 ans
    case1:我们把一个0 变成1 那么这次改变对答案的贡献为 后面所有的0 - 前面所有的1 
    case2:我们把一个1 变成0 那么这次改变对答案的贡献为 前面所有的1 - 后面所有的0
    然后把所有的情况都算出来 得到一个最大值 加到ans上 详情见代码 时间复杂度(O(n))
    
  • 代码

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    const int N = 2e5 + 10;
    const int mod = 1e9 + 7;
    int sum[N];  //sum[i] 代表前i个数有多少个1
    void solve()
    
        int n;
        cin >> n;
        vector<int> vec(n + 1);
        for (int i = 1; i <= n; i++)
        
            sum[i] = 0;
        
        for (int i = 1; i <= n; i++)
        
            cin >> vec[i];
            sum[i] = sum[i - 1] + vec[i];
        
        int ans = 0;
        for (int i = 1; i <= n; i++)  //先计算 不变的情况下的答案
        
            if (vec[i] == 1)
            
                ans += ((n - i) - (sum[n] - sum[i]));
            
        
        int maxx = 0;
        for (int i = 1; i <= n; i++)
        
            if (vec[i] == 0)
            
                maxx = max(maxx, ((n - i) - (sum[n] - sum[i])) - sum[i]); //逻辑 见思路
            
            else if (vec[i] == 1)
            
                maxx = max(maxx, sum[i - 1] - ((n - i) - (sum[n] - sum[i]))); //逻辑 见思路 
            
        
        cout << maxx + ans << \'\\n\';
    
    signed main()
    
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
        int __ = 1;
        cin >> __;
        while (__--)
        
            solve();
        
        return 0;
    
    

E 题目链接

  • 题目大意

    有n个任务。如果你完成第i个任务,你将获得ai币。你每天最多只能完成一个任务。然而,一旦你完成了一个任务,在K天内你不能再做同样的任务。(例如,如果k=2,你在第1天做了任务1,那么你在第2天或第3天不能做,但你可以在第4天再做)。
    给你两个整数c和d。找出k的最大值,使你在d天内至少能获得c个硬币。如果不存在这样的k,则输出Impossible。如果k可以是任意大的,则输出Infinity。
    
  • 思路

    二分答案
    得到数组后先排序 
    如果最大的a[i]*d < c的话 那么无解 输出 Impossible
    如果排序后数组的 前d 个数的和 > c 输出 Infinity
    
    然后进行二分答案 具体的check函数 见代码 
    需要注意的是 代码中min函数对数组访问越界的控制 
    
  • 代码

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    const int N = 2e5 + 10;
    const int mod = 1e9 + 7;
    int vec[N];
    int c, d;
    int n;
    bool check(int k)
    
        k += 1;
        int sy = d - (d / k) * k;  //进行k轮后剩余的天数 等效于 sy = d % k 也可以
        int js = 0;
        for (int i = 1; i <= min(k, n); i++)
        
            js += vec[i];
        
        js *= (d / k);
        for (int i = 1; i <= min(sy, n); i++)
        
            js += vec[i];
        
        return js >= c;
    
    void solve()
    
        cin >> n >> c >> d;
        for (int i = 1; i <= n; i++)
        
            cin >> vec[i];
        
        sort(vec + 1, vec + n + 1, greater<int>());
        if (vec[1] * d < c)
        
            cout << "Impossible" << \'\\n\';
            return;
        
        else
        
            int ans = 0;
            for (int i = 1; i <= min(n, d); i++)  //不能直接 i<=d 因为d可能比n大 导致访问越界 其他地方的min函数作用亦然
            
                ans += vec[i];
            
            if (ans >= c)
            
                cout << "Infinity" << \'\\n\';
                return;
            
            else
            
                int anss = 0;
                int l = 0, r = 1e9 + 10;
                while (l <= r)
                
                    int mid = l + r >> 1;
                    if (check(mid))
                    
                        anss = mid;
                        l = mid + 1;
                    
                    else
                    
                        r = mid - 1;
                    
                
                cout << anss << \'\\n\';
            
        
    
    signed main()
    
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
        int __ = 1;
        cin >> __;
        while (__--)
        
            solve();
        
        return 0;
    
    

F题目链接

  • 题目大意

     给你一棵树和两个点α,b,边有边权。你可以在任意时刻从当前所在的点跳到任意除了b以外的点。求有没有方案使得从a出发,到达b时边权X0r和为0。
    
  • 思路

    首先我们确定一个共识 就是一条边不会被走两次 因为重走一条边没有任何意义 x^x=0 并且这是一棵树 是不会出现环的
    我们考虑先从b出发做一遍 bfs 在bfs的过程中 我们可以得到很多的 x,y x代表当前走到的点 y代表走到这个点的val
    我们定义一个 map<int> mp 对于每一个x,y 我们让 mp[y]=1;
    
    我们再考虑从a点出发 做一遍bfs 我们会获得很多的 x1,y1 x1代表当前走到的点 y1代表走到这个点的val。如果mp[y1]=1的话,我们就可以直接飞到一个点 使得最后走到b的异或和为0; 
    
  • 代码

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    const int N = 1e5 + 10;
    const int mod = 1e9 + 7;
    vector<pair<int, int>> vec[N];
    int vis[N];
    void solve()
    
        map<int, int> mp;
        int n;
        int a, b;
        cin >> n >> a >> b;
        for (int i = 1; i <= n; i++)
        
            vis[i] = 0;
            vec[i].clear();
        
        for (int i = 1; i <= n - 1; i++)
        
            int u, v, val;
            cin >> u >> v >> val;
            vec[u].push_back(v, val);
            vec[v].push_back(u, val);
        
        queue<pair<int, int>> que;
        que.push(b, 0);
        vis[b] = 1;
        while (que.size())
        
            auto u = que.front();
            que.pop();
            if (u.first != b)
                mp[u.second] = 1;
            for (int i = 0; i < vec[u.first].size(); i++)
            
                if (!vis[vec[u.first][i].first])
                
                    vis[vec[u.first][i].first] = 1;
                    que.push(vec[u.first][i].first,
                              vec[u.first][i].second ^ u.second);
                
            
        
        for (int i = 1; i <= n; i++)
            vis[i] = 0;
        que.push(a, 0);
        vis[a] = 1;
        vis[b] = 1;
        int flag = 0;
        while (que.size())
        
            auto u = que.front();
            if (mp[u.second])
            
                flag = 1;
                break;
            
            que.pop();
            for (int i = 0; i < vec[u.first].size(); i++)
            
                if (!vis[vec[u.first][i].first])
                
                    vis[vec[u.first][i].first] = 1;
                    que.push(vec[u.first][i].first,
                              vec[u.first][i].second ^ u.second);
                
            
        ;
        if (flag == 1)
            cout << "YES\\n";
        else
            cout << "NO\\n";
    
    signed main()
    
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
        int __ = 1;
        cin >> __;
        while (__--)
        
            solve();
        
        return 0;
    
    

G 题目链接

G题纯签到就不讲了。

题解写的有错误还请指正。

NOIP模拟 (8-2情人节欢乐赛) 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置。

题目来自hzwer的模拟题

 

果实计数

 

count.pas/.c/.cpp

 

时间限制:1s,空间限制32MB

 

题目描述:

 

淘淘家有棵奇怪的苹果树,这棵树共有n+1层,标号为0~n。这棵树第0层只有一个节点,为根节点。已知这棵树为b叉树,且保证是一颗满b叉树

 

现在,该树第n层的每个节点上都结出了一个苹果,淘淘想知道共结了多少苹果。由于数量可能很大,答案要求输出mod k后的结果。

 

输入描述:

 

给出1的节点数b和层数nk.

 

输出描述:

 

输出苹果数mod k后的结果。

 

样例输入:

 

2 10 9

 

样例输出:

 

7

 

数据范围:

 

30%的数据保证:b<=100,n<=10, k<=100.

 

100%的数据保证:b<2^31,n<2^31,k<=2^15.

 

分析:

快速幂裸题。

 

AC代码:

 

 1 #include<algorithm>
 2 #include<cmath>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<algorithm>
 6 
 7 inline void read(long long &x)
 8 {
 9     char ch = getchar(),c = ch;x = 0;
10     while(ch < 0 || ch > 9) c = ch,ch = getchar();
11     while(ch <= 9 && ch >= 0) x = (x<<1)+(x<<3)+ch-0,ch = getchar();
12     if(c == -) x = -x;
13 } 
14 
15 long long a,n,k;
16 
17 int ksm(int n)
18 {
19     long long r = a;
20     for(;n;n>>=1)
21     {
22         if(n&1)
23             r = r*a%k;
24         a = a*a%k;
25     }
26     return r%k;
27 }
28 
29 int main()
30 {
31     read(a),read(n),read(k);
32     //最终果实数 = a^(n-1)%k
33     int ans = ksm(n-1)%k;
34     printf("%d\n",ans);
35     return 0;
36 }

 

 

 

 

打地鼠游戏

 

mouse.pas/.c/.cpp

 

时间限制:1s 空间限制:128MB

 

题目描述:

 

伟大的2320学长特别喜欢打地鼠游戏,这个游戏开始后,会在地板上冒出一些地鼠来,你可以用榔头去敲击这些地鼠,每个地鼠被敲击后,将会增加相应的游戏分值。可是,所有地鼠只会在地上出现一段时间(而且消失后再也不会出现),每个地鼠都在0时刻冒出,但停留的时间可能是不同的,而且每个地鼠被敲击后增加的游戏分值也可能是不同。

 

最近2320学长经常玩这个游戏,以至于敲击每个地鼠只要1秒。他在想如何敲击能使总分最大。

 

输入描述:

 

输入包含3行,第一行包含一个整数n1<=n<=100000)表示有n个地鼠从地上冒出来,第二行n个用空格分隔的整数表示每个地鼠冒出后停留的时间(Maxt<=50000),第三行n个用空格分隔的整数表示每个地鼠被敲击后会增加的分值vv<=1000)。每行中第i个数都表示第i个地鼠的信息。

 

样例输入:

 

5

 

5 3 6 1 4

 

7 9 2 1 5

 

样例输出:

 

24

 

数据范围:

 

30%的数据保证n<=100, t<=500,v<=50

 

60%的数据保证 n<=10000,t<=3000,v<=500

 

100%的数据保证 n<=100000,t<=5000,v<=1000

 

分析:

这题没有A...而且坚持认为自己的思路是对的,很郁闷。

正解思路:堆维护的贪心。把所有鼹鼠的参数放到一个大根堆里,然后按照时间顺序,依次取出堆中元素,在每秒钟都敲击当前能敲的分数最高的鼹鼠。

我的思路:贪心,按照分数给鼹鼠排序,用vis数组记录某个时间是否已经敲了鼹鼠。然后枚举每一只鼹鼠,并且尝试在它消失的那一秒敲击它。如果它消失的那一秒已经被使用,就往前找,找到最近的一个时间来敲击它并标记。

未能AC代码并求指错(如果是思路错了也请指教...):

 

 1 #include<algorithm>
 2 #include<cmath>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<algorithm>
 6 
 7 inline void read(int &x)
 8 {
 9     char ch = getchar(),c = ch;x = 0;
10     while(ch < 0 || ch > 9) c = ch,ch = getchar();
11     while(ch <= 9 && ch >= 0) x = (x<<1)+(x<<3)+ch-0,ch = getchar();
12     if(c == -) x = -x;
13 } 
14 
15 struct MOLE
16 {
17     int t,v;
18 }a[100005];
19 
20 int vis[100005];
21 
22 int cmp(MOLE a,MOLE b)
23 {
24     if(a.v == b.v)
25         return a.t > b.t;
26     return a.v > b.v;
27 }
28 
29 int main()
30 {
31 //    freopen("1.txt","r",stdin);
32     int n,ans = 0;
33     read(n);
34     for(register int i = 1;i <= n;++ i)
35         read(a[i].t);
36     for(register int i = 1;i <= n;++ i)
37         read(a[i].v);
38     std::sort(a+1,a+1+n,cmp);
39     for(register int i = 1;i <= n;++ i)
40     {
41         if(!vis[a[i].t])
42             vis[a[i].t] = 1,ans += a[i].v;
43         else
44         {
45             for(register int t = a[i].t;t > 0;-- t)
46             {
47                 if(!vis[t])
48                     vis[t] = 1,ans += a[i].v;
49             }
50         }
51     }
52     printf("%d\n",ans);
53     return 0;
54 }

 

 

 

斗牛

 

niuniu.pas/.c/.cpp)

 

时间限制:2s,空间限制:128MB

 

题目描述:

 

为了更快的获取欢乐豆(因为本蒟蒻斗地主水平太低233),hzwer准备去玩欢乐斗牛,但是由于rp太差,hzwer在一个小时之内输光了20QQ号的欢乐豆(每天系统会赠送每个号4000欢乐豆)。第二天他准备继续再战欢乐斗牛的抢庄模式,但是由于缺乏思考能力,hzwer需要编写一个程序来决定是否抢庄。

 

在玩家决定是否抢庄之前,系统会下发四张牌称为底牌,最后一张牌在决定后发放,每张牌可能为1-10JQKhzwer认为最后一张牌为每一种点数的概率是相同的,对于一个由五张牌组成的牌型,分数计算规则如下,请你得出底牌的期望得分。

 

首先注意:在斗牛中,JQK的点数视为10点,即111213在计算头或点数时均视为10,所有牌无视其花色。

 

首先考虑特殊牌型

 

  1. 四炸——5张牌中有4张一样的牌(33334)分数为40
  2. 五花牛——五张牌均是JQK(如JQJQK)分数为50
  3. 五小牛——五张牌点数都小于5且点数和小于或等于1011223)分数为60

 

若有多种特殊牌型,得分取分数最大的特殊牌型(如11112视为五小牛)。

 

如果没有特殊牌型,首先判断牌型是否有,如果五张牌中任意三张的总和为10的倍数如(1K9)即为有,无的牌型得分为0

 

对于有头的牌型得分计算如下:

 

所有牌的和记为t,如果t%10=0则称为牛牛”,牛牛得分为30t%10<7称为小牛,得分为t%10,否则得分为(t%10*2

 

输入描述:

 

第一行一个整数T,表示T组数据

 

每组数据占一行,为4个整数(111213分别表示JQK

 

输出描述:

 

对于输入的n行,输出每4张牌的期望得分(四舍五入)

 

样例输入:

 

2

 

2 2 2 2

 

10 4 5 12

 

样例输出:

 

43

 

9

 

样例解释:

 

对于2 2 2 2,最后一张为12时,构成五小牛,否则为炸弹,期望得分为(2*60+11*40)/13=43.08

 

对于10 4 5 12,最后一张为1-13的得分分别是30+0+0+0+4+5+0+0+0+18+18+18+18=111/13=8.54

 

1为牛牛,54点,65点,10-139点,其余无头

 

数据范围:

 

30%的数据T<=5

 

70%的数据T<=100000

 

100%的数据T<=1000000

 

蒟蒻感言:

 

在某次对局中发现期望得分很高,果断抢了庄,但是发现有闲家3牛牛,瞬间消失20W欢乐豆

 

分析:

可怕的大暴力...

先用一个五维数组预处理出所有可能的得分,然后在求每一组数据的时候直接求和再/13即可。注意四舍五入。

 

AC代码:

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cstdlib>
 5 
 6 inline void read(int &x)
 7 {
 8     x = 0;char ch = getchar(), c = ch;
 9     while(ch < 0 || ch > 9)c = ch, ch = getchar();
10     while(ch <= 9 && ch >= 0)x = x * 10 + ch - 0, ch = getchar();
11     if(c == -)x = -x;
12 }
13 
14 inline int min(int a,int b)
15 {return a<b?a:b;}
16 
17 int ans[15][15][15][15][15];
18 int x,y,m,n,z,sum;
19 
20 inline void init()
21 {
22     for(int a = 1;a <= 13;++ a)
23         for(int b = 1;b <= 13;++ b)
24             for(int c = 1;c <= 13;++ c)
25                 for(int d = 1;d <= 13;++ d)
26                     for(int e = 1;e <= 13;++ e)
27     {
28         if(a<5 && b<5 && c<5 && d<5 && e<5 && a+b+c+d+e <= 10)
29             ans[a][b][c][d][e] = 60;
30         else if(a>10 && b>10 && c>10 && d>10 && e>10)
31             ans[a][b][c][d][e] = 50;
32         else if((a==b && b==c && c==d) || (a==b && b==c && c==e) || (a==b && b==d && e==d) || (a==e && e==c && c==d) || (e==b && b==c && c==d))            
33             ans[a][b][c][d][e] = 40;
34         else
35         {
36             x = min(a,10),y = min(b,10),m = min(c,10),n = min(d,10),z = min(e,10);
37             if((x+y+m)%10 == 0 || (y+m+n)%10 == 0 || (m+n+z)%10 == 0 || (x+y+n)%10 == 0 || (x+y+z)%10 == 0 || (y+m+z)%10 == 0 || (y+n+z)%10 == 0 || (x+n+z)%10 == 0 || (x+m+n)%10 == 0 || (x+m+z)%10 == 0)
38             {
39                 sum = x+y+m+n+z;
40                 if(sum%10 == 0)
41                     ans[a][b][c][d][e] = 30;
42                 else if(sum%10 < 7)
43                     ans[a][b][c][d][e] = sum%10;
44                 else
45                     ans[a][b][c][d][e] = (sum%10)*2;
46             }
47         }
48     }
49 }
50 
51 int main()
52 {
53     init();
54     int t;
55     read(t);
56     for(;t;--t)
57     {
58         sum = 0;
59         read(x),read(y),read(m),read(n);
60         for(register int i = 1;i <= 13;++ i)
61             sum += ans[x][y][m][n][i];
62         sum = (sum/13.0)+0.5;
63         printf("%d\n",sum);
64     }
65     return 0;
66 }

 

 

 

 

以上是关于欢乐结训赛题解的主要内容,如果未能解决你的问题,请参考以下文章

NOIP模拟 (8-2情人节欢乐赛) 题解

2019欢乐友谊赛A题题解

暑假爆零欢乐赛SRM08题解

欢乐的跳——快看,远处的学妹正向你慢慢走来

牛客 | 一起来做题~欢乐赛2 (AK 题解)

Comet OJ 1023 [欢乐赛]第001话 宝可梦,就决定是你了!题解