BUCT-2023寒假集训-进阶训练赛(九)题解

问题 A: 3-9 So Easy

题目描述

G先生发明了一个新游戏,游戏规则如下。 首先,他有一个nxn的矩阵,所有的元素一开始都是0.然后,他继续执行一些操作:每次选择一行或者一列,并向所选行或列中的所有元素添加一个任意的正整数。当所有操作完成后,他在矩阵中隐藏一个元素,这个元素被修改为-1,现在给出最后一个矩阵,要求你在最后一个隐藏操作之前找出隐藏元素。

输入

第一行包含一个整数n(2<=n<=1000),接下来的n行表示操作后的矩阵。矩阵中的每个元素满足-1<=aij<=1000000,且恰好有一个元素是-1.

输出

一个整数,即隐藏元素。

样例输入
3
1 2 1
0 -1 0
0 1 0
样例输出
1

思路

将位置分为四种情况分别计算,注意边界问题。

由相邻正方形上四个数对角线分别相加相等的规律可求得任意一个数。

代码

#include<bits/stdc++.h>
using namespace std;

const int INF=0x3fffffff;

int main()
{
    ios::sync_with_stdio(false); 
    cin.tie(0);
    cout.tie(0);
    int n;
    cin>>n;
    vector<vector<int> > a(n,vector<int>(n));
    int x,y;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            cin>>a[i][j];
            if(a[i][j]==-1){
                x=i;
                y=j;
            }
        }
    }
    int ans;
    if(x<n-1 && y<n-1){
        ans=a[x+1][y]+a[x][y+1]-a[x+1][y+1];
    }
    else if(x==n-1 && y!=n-1){
        ans=a[x-1][y]+a[x][y+1]-a[x-1][y+1]; 
    }
    else if(y==n-1 && x!=n-1){
        ans=a[x+1][y]+a[x][y-1]-a[x+1][y-1];
    }
    else if(x==n-1&& y==n-1){
        ans=a[x-1][y]+a[x][y-1]-a[x-1][y-1];
    }
    cout<<ans;
}

问题 B: 用递归的方法求值

题目描述

求1+2+3+4+5+...+n的值 。

输入

一个n(n不大于10000)

输出

输出1到n的累加和

样例输入
2
样例输出
3

思路

可以直接用公式n*(n+1)/2

也可以用递归的方法,n=1时,返回1,n!=1时,返回n+f(n-1)

代码

#include<bits/stdc++.h>
using namespace std;

int f(int n){
    if(n==1)    return 1;
    else{
        return n+f(n-1);
    }
}

int main()
{
    ios::sync_with_stdio(false); 
    cin.tie(0);
    cout.tie(0);
    int n;
    cin>>n;
    cout<<f(n);
}



/**
int main()
{
    ios::sync_with_stdio(false); 
    cin.tie(0);
    cout.tie(0);
    int n;
    cin>>n;
    cout<<n*(n+1)/2;
}
**/

问题 C: 字符串移位包含问题

题目描述

对于一个字符串来说,定义一次循环移位操作为:将字符串的第一个字符移动到末尾,形成新的字符串。

给定两个字符串s1和s2,要求判定其中一个字符串是否是另一个字符串通过若干次循环移位后的新字符串的子串,例如CDAA是由AABCD两次以为后产生的新船BCDAA的字串,而ABCD与ABCD则不能通过多西移位使得其中一个字符串是新串的子串。

输入

一行,包含两个字符串,中间由单个空格隔开。字符串只包含字母和数字,长度不超过30.

输出

如果一个字符串是另一个字符串通过若干次移位产生的新串的子串,则输出true,否则输出false

样例输入
AABCD CDAA
样例输出
true

思路

将原字符串加在后面,从头向后遍历寻找是否有与给定子串相同的子串。

代码

#include<bits/stdc++.h>
using namespace std;

int main()
{
    ios::sync_with_stdio(false); 
    cin.tie(0);
    cout.tie(0);
    string s;
    string t;
    cin>>s>>t;
    s+=s;
    for(int i=0;i<s.size();i++){
        int j;
        for(j=0;j<t.size();j++){
            if(s[i+j]!=t[j]){
                break;
            }
        }
        if(j>=t.size()){
            cout<<"true";
            return 0;
        }
    }
    cout<<"false";
}

问题 D: LELE的RPG难题

题目描述

人称"AC女之杀手"的超级偶像 LELE最近忽然玩起了深沉,这可急坏了众多Cole(LELE 的粉丝,即"可乐"),经过多方打探,某资深 Cole终于知道了原因,原来,LELE最近研究起了著名的RPG难题。有排成一行的n个方格,用红(Red)、粉(Pink)和绿(Green)三色涂每个格子,每格涂一个颜色,要求任何相邻的方格不能同色,且首尾两格也不同色,求全部的满足要求的涂法。以上就是著名的RPG难题。如果你是 Cole,我想你一定会想尽办法帮助LELE解决这个问题的;如果不是,那么看在众多漂亮的痛不欲生的Cole 的面子上,你也不会袖手旁观吧?

输入

输入数据包含多个测试样例,每个测试样例占一行,由一个整数n组成(0<n≤50)

输出

对于每个测试样例,请输出全部的满足要求的涂法,每个样例的输出占一行。

样例输入
1
3
样例输出
3
6

思路

本题采用递推的思想。

dp[i]表示第i个方格与第一个方格颜色不同的方案数量。

除了1,2,3的值是3,6,6以外,其他的都符合dp[i] = dp[i-1]+2*dp[i-2]。

如果第i-1个方格和第一个方格颜色相同,则第i-2个方格和第i-1个方格颜色不同,即与第一个方格颜色不同,此时第i个格子可以涂两种颜色,方案数为2*dp[i-2]。

如果第i-1个方格和第一个方格颜色不同,此时第i个格子只能涂一种颜色,方案数为1*dp[i-1]

代码

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
 
int main()
{
    ios::sync_with_stdio(false); 
    cin.tie(0);
    cout.tie(0);
    int n;
    ll dp[50+5];
    dp[1]=3,dp[2]=6,dp[3]=6;
    for(int i=4;i<=50;i++){
        dp[i]=dp[i-1]+2*dp[i-2];
    }
    while(cin>>n){
        cout<<dp[n]<<endl;
    }
} 

问题 E: [蓝桥杯2015初赛]三羊献瑞

题目描述

观察下面的加法算式:

其中,相同的汉字代表相同的数字,不同的汉字代表不同的数字。

请你填写“三羊献瑞”所代表的4位数字(答案唯一),不要填写任何多余内容。

输出

请你填写“三羊献瑞”所代表的4位数字(答案唯一),不要填写任何多余内容。

思路

用next_permutation( ) 求[0,9]的全排列,每个位置对应每个字分别计算三个数,其中要保证t2为四位数。

代码

#include<bits/stdc++.h>
using namespace std;

int main()
{
    ios::sync_with_stdio(false); 
    cin.tie(0);
    cout.tie(0);
    int a[10];
    for(int i=0;i<10;i++){
        a[i]=i;
    }
    //祥 瑞 生 辉 三 羊 献 气
    int t1,t2,t3;
    do{
        if(a[4])  //保证s2是四位数
        {
            t1=a[0]*1000+a[1]*100+a[2]*10+a[3];
            t2=a[4]*1000+a[5]*100+a[6]*10+a[1];
            t3=a[4]*10000+a[5]*1000+a[2]*100+a[1]*10+a[7];
        }    
        if(t1+t2==t3){
            break;
        }
    }while(next_permutation(a,a+10));
    printf("%d",t2);
    return 0;
}

问题 F: [蓝桥杯2015初赛]生命之树

题目描述

在X森林里,上帝创建了生命之树。

他给每棵树的每个节点(叶子也称为一个节点)上,都标了一个整数,代表这个点的和谐值。

上帝要在这棵树内选出一个非空节点集S,使得对于S中的任意两个点a,b,都存在一个点列 {a,v1,v2,...vk,b}

使得这个点列中的每个点都是S里面的元素,且序列中相邻两个点间有一条边相连。

在这个前提下,上帝要使得S中的点所对应的整数的和尽量大。

这个最大的和就是上帝给生命之树的评分。

经过atm的努力,他已经知道了上帝给每棵树上每个节点上的整数。

但是由于 atm 不擅长计算,他不知道怎样有效的求评分。

他需要你为他写一个程序来计算一棵树的分数。

输入

第一行一个整数 n 表示这棵树有 n 个节点。(0<n≤105)

第二行 n 个整数,依次表示每个节点的评分。(每个节点的评分不超过106)

接下来 n-1 行,每行 2 个整数 u, v,表示存在一条 u 到 v 的边。

由于这是一棵树,所以是不存在环的。

输出

输出一行一个数,表示上帝给这棵树的分数。

样例输入
5
1 -2 -3 4 5
4 2
3 1
1 2
2 5
样例输出
8

思路

本题为树状dp

对于一个无环的连通图,我们可以取任意的一点作为根节点来构建一棵树,然后搜索这棵树,找到最大的连通块。

用f[u]表示以u为父节点包含u结点的所有连通块的最大值。用w[i]来表示点i的权值。

以题目中给的样例建立一棵树

要求f[1]的最大值,就要求所有子树的最大权值加上w[i],再向下推就是求f[2]的最大值,即求以2为父节点的所有子树的最大权值和...

本题的关键公式为:f[u] = w[u] + max(ff[1] + ff[2] + ···+f[u])(所有子树的最大权值和)

代码

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=1e5+5;
int n;
ll f[N],w[N];  
vector<int> v[N];
ll res=0;

void dfs(int u,int father){
    for(int i=0;i<v[u].size();i++){
        int son=v[u][i];
        if(son!=father){
            dfs(son,u);
            f[u]+=max((ll)0,f[son]);
        }
    }
    res=max(res,f[u]);
}

int main()
{
    ios::sync_with_stdio(false); 
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>w[i];
        f[i]=w[i]; 
    }
    for(int i=1;i<=n-1;i++){
        int from,to;
        cin>>from>>to;
        v[from].push_back(to);
        v[to].push_back(from);
    }
    dfs(1,-1);
    cout<<res<<endl;
    return 0;
}

问题 G: Dongdziz与不说阿拉伯语的阿拉伯商人

题目描述

Dongdziz到一个阿拉伯人开的商店购物。

这个阿拉伯人说:” Jestem tak smutny, że nie skończyłem pracy domowej i muszę przygotować się do cotygodniowego konkursu. Nie mam nawet dziewczyny. Kiedy mogę zdjąć zamówienie? Czy mogę przejść na emeryturę? Dlaczego powinienem grać w konkursie? Nie rób tego. Chcę spać. W porządku. Nie będziesz się nudzić wystarczająco, aby tłumaczyć ten paragraf. Po prostu wypluję go tutaj. Nikt nie widzi QAQ i tak.

Dongdziz感到很奇怪,明明这并不是阿拉伯语啊,但是聪明的他听懂了这个人想要说什么,

这个阿拉伯人会卖1e9个游戏,每个游戏都有个标号,比如3号游戏标号就是3,1e9号游戏标号就是1e9(从1开始

计算标号),但是每个游戏的价格是这么算的,价格=游戏标号*买家的年龄A+游戏标号的位数*商人的年龄B

不用担心,我们会知道dongdziz哥哥的年龄和商人的年龄的

问题来了,dongdziz今天钱包里只有x元钱,问能最买的标号最大的游戏是哪个呢?

如果哪个游戏都买不了,那么请输出0

输入

一行 三个数

A B x

1<=A<=1e9

1<=B<=1e9

1<=x<=1e18

输出

能最买的标号最大的游戏的标号

样例输入
10 7 100
样例输出
9

思路

标号为1-1e9,即最多有10位数字,最大为1e9

先判断是否能买,即x>=A+B

若不能买输出0;

若能买则:从0-10对每个位数遍历,

由价格=游戏标号*买家的年龄A+游戏标号的位数*商人的年龄B,

可得 游戏标号=(游戏标号-游戏标号的位数*商人的年龄B)/买家的年龄A

每次都找最大值,记得控制范围。

代码

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
 
int main()
{
    ios::sync_with_stdio(false); 
    cin.tie(0);
    cout.tie(0);
    ll a,b,x;
    cin>>a>>b>>x;
    if(x<a+b){
        cout<<0;
        return 0;
    }
    ll ans=0;
    for(int i=1;i<=10;i++){
        ll t=(x-i*b)/a;
        if(t>=pow(10,i)){
            t=pow(10,i)-1;
        }
        ans=max(ans,t);
    }
    ans=min(ans,(ll)1000000000);
    cout<<ans;
}

问题 H: 回文游戏

题目描述

给你一个字符串S,

你可以修改S的任意一个字符,

问最少需要修改多少次,使得S变成一个回文串。

输入

字符串S

1<=长度<=100

输出

最少需要修改多少次,使得S变成一个回文串。

样例输入
redcoder
样例输出
1

思路

从中间向两边找,如果字符不相同,则ans+1;

奇数与偶数的起点不一样,偶数从中间两个向外扩散,奇数则忽略正中间的字符再向前找

代码

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
 
int main()
{
    ios::sync_with_stdio(false); 
    cin.tie(0);
    cout.tie(0);
    string s;
    cin>>s;
    int l,r,ans=0;
    if(s.size()%2==0){
        l=s.size()/2-1;
        r=s.size()/2;
    } 
    else{
        l=s.size()/2-1;
        r=s.size()/2+1;
    }
    while(l>=0){
        if(s[l]!=s[r]){
            ans++;
        }
        l--,r++;
    }
    cout<<ans;
}

问题 I: 好人 or 坏人 ?

题目描述

有n个人在玩一个游戏,

这个游戏是这样的,我们将n个人分为好人和坏人,也有可能n个人都是好人或者都是坏人

好人说的话一定是对的,坏人说的话有可能对,也有可能不对。

现在给你每个人对另外一些人的判断,

问好人最多有多少个

输入

第一行输入n , 表示有n个人

接下来若干行 , 每一行输入一个Ai

Ai表示 i 这个人说了Ai条信息,

接下来Ai行,每一行输入一个Xi, Yi

如果Xi= 1

表示 i 这个人认为Xi这个人是好人

如果Yi= 0

表示 i 这个人认为Xi这个人是坏人

1 <= n <= 15

0 <= Ai <= n - 1

1 <= Xi <= n

0 <= Yi <= 1

输出

好人最多有多少个

样例输入 复制
3
1
2 1
1
1 1
1
2 0
样例输出 复制
2
提示

如果第1人和第2人是好人,第3人是坏人,这是最大可能的好人人数。

样例2 :

输入:

3

2

2 1

3 0

2

3 1

1 0

2

1 1

2 0

输出:

0

思路

从中间向两边找,如果字符不相同,则ans+1;

奇数与偶数的起点不一样,偶数从中间两个向外扩散,奇数则忽略正中间的字符再向前找

代码

问题 J: 双刀流银色战车

题目描述

银色战车+阿努比斯神是波波仅有的高光时刻,那么波鲁纳雷夫玩节奏光剑的话会怎么样呢?

给出两个长度均为n字符串,L表示所有的左手键需要砍的字符,R表示所有的右手键需要砍的字符。

波波挥剑时遵循左-右-左-右……的顺序。

那么字符出现的顺序应该如何?

输入

1≤n≤100

|L|==|R|==n

输出

总的字符串

样例输入
2
ip cc
样例输出
icpc

思路

根据题意模拟即可,从前向后遍历,先输出第一个字符串的字符,再输出第二个字符串的字符。

代码

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
 
int main()
{
    ios::sync_with_stdio(false); 
    cin.tie(0);
    cout.tie(0);
    int n;
    cin>>n;
    string s1,s2;
    cin>>s1>>s2;
    for(int i=0;i<n;i++){
        cout<<s1[i]<<s2[i];
    } 
    return 0; 
}

问题 K: 点外卖

题目描述

小L和朋友们想趁着疯狂星期八薅羊毛。

他们一共点了n样,每样的价钱是Ai(1≤i≤n)

疯狂星期八的优惠措施是这样的:店家会给你m张优惠劵,优惠券的作用是将某样的价格打对折(下取整),并且它是可以叠加的。

那么小L他们最少需要多少钱。

输入

n道菜,m张券,第i道菜的价格是x。

1≤n,m≤105

1≤Ai≤109

输出

最少要花多少钱

样例输入
4 4
1 9 3 5
样例输出
6

思路

由于优惠券可以叠加使用,所以采用优先队列,每次都对队头最大的元素折半处理,放入队列之中,最后将队列中元素相加即可。

代码

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

int main()
{
    ios::sync_with_stdio(false); 
    cin.tie(0);
    cout.tie(0);
    int n,m;
    cin>>n>>m;
    priority_queue <int> q;
    for(int i=0;i<n;i++){
        int t;
        cin>>t;
        q.push(t);
    } 
    while(m--){
        int t=q.top();
        t/=2;
        q.pop();
        q.push(t);
    } 
    ll sum=0; 
    while(q.size()){
        sum+=q.top();
        q.pop();
    }
    cout<<sum<<endl;
}

问题 L: 祖玛的复仇

题目描述

小L刚睡醒,就被他的狐朋狗友拖去玩祖玛。

在这个版本中,单单消除相同颜色的球已经不够看了。它需要找出在长度为N的原字符串S中出现两次或两次以上的连续子字符串才可以。但是祖玛的目标是把它消除掉,所以这两串连续子字符串是不允许相交的。

说人话就是,找出最大的len,使其存在两个整数a,b(1≤a,b≤N−len+1):

a+len≤b

S[a+i]==S[b+i],(i=0,1,……len−1)

没有符合条件的len的话就输出0

输入

1≤n≤5000

字符串均为小写字符

输出

len的最大长度

样例输入
5
ababa
样例输出
2
提示

稍微选择了一个比较容易懂的样例。

可以看出,符合题目条件的连续子字符串有:a、b、ab、ba。

比如aba,它虽然是重复出现的连续子字符串,但是两个子字符串之间的重叠是不可避免的,因此不合法。

那么最长的len也就是2了。

思路

在长度为 n 的原字符串 S 找到中出现两次或两次以上的、相互之间没有重叠的连续子字符串的最大长度。

考虑双指针,但是暴力必然会超时O(n^3)。

题面求len的最大长度,可考虑二分法求长度,再用双指针寻找,此时时间复杂度为O(logn* n^2)。想要通过此题,字符串比较需要在O(1)内完成,使用Hash来处理。

代码

#include <bits/stdc++.h>
using namespace std;
#define ull unsigned long long
const int N = 5e3 + 10, M = 1e6 + 10, p = 131;
int n;
char s[N];
ull h[M], d[M];

void init() {
    d[0] = 1, h[0] = 0;
    for (int i = 1; i <= n; i++) {
        h[i] = h[i - 1] * p + s[i] - 'a' + 1;
        d[i] = d[i - 1] * p;
    }
}

ull get(int l, int r) {
    return h[r] - h[l - 1] * d[r - l + 1];
}

bool check(int mid) {
    for (int i = 1; i <= n - mid + 1; i++) {
        ull x = get(i, i + mid - 1);
        for (int j = i + mid; j <= n - mid + 1; j++) {
            ull y = get(j, j + mid - 1);
            if (x == y)
                return true;
        }
    }
    return false;
}

int main() {

    cin >> n >> s + 1;
    init();

    int l = 0, r = n;
    while (l < r) {
        int mid = l + r + 1 >> 1;
        if (check(mid))
            l = mid;
        else
            r = mid - 1;
    }
    cout << l;
    return 0;
}

问题 M: 异或和

题目描述

在摆了一个寒假之后,小L突然想起来自己的训练题单一道没刷,坏了,出大问题,但是第一道题他就不会:

有n个数,需要你把他们分为两个非空的集合a和b,然后要求出a中所有数的异或和与b中所有数的异或和的和的最大值。

小L知道你是一位佬,所以快教教他吧,Orz。

输入

2≤n≤105

1≤x<260

输出

异或的和的最大值

样例输入 复制
3
3 6 5
样例输出 复制
12

(以下为大佬的思路和代码)

思路

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10, M = 350, mod = 1e9 + 7;
#define LL long long
int n;
LL s[N];
LL p[110];

void add(LL x) {
    for (int i = 62; i >= 0; i--) {
        if (!(x >> i))
            continue;
        if (!p[i]) {
            p[i] = x;
            break;
        }
        x ^= p[i];
    }
    return ;
}

signed main() {

    cin >> n;
    LL sum = 0;
    for (int i = 1; i <= n; i++)
        scanf("%lld", &s[i]), sum ^= s[i];

    for (int i = 62; i >= 0; i--)
        if (sum >> i & 1ll) {
            for (int j = 1; j <= n; j++)
                if (s[j] >> i & 1ll)
                    s[j] ^= (1ll << i);
        }

    for (int i = 1; i <= n; i++)
        add(s[i]);

    LL ans = 0;
    for (int i = 62; i >= 0; i--)
        if ((ans ^ p[i]) > ans)
            ans ^= p[i];
    cout << (ans ^ sum) + ans;
    return 0;
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值