2021年第十二届蓝桥杯大赛软件赛决赛C/C++大学A组 个人部分题解

题目下载链接:https://download.csdn.net/download/ljw_study_in_CSDN/19403461

这次国赛,基本上大题都是暴力写的,填空题只写了前两个,老混子选手了

不一定是正确题解,本文抛砖引玉,欢迎大家讨论解法,批评指正~

文章目录

    • A题 纯质数(5分)
    • B题 完全日期(5分)
    • C题 最小权值(10分)
    • D题 覆盖(10分)
    • E题 123(15分)
    • F题 异或变换(15分)
    • G题 冰山(20分)
    • H题 翻转括号序列(20分)
    • I题 异或三角(25分)
    • J题 积木(25分)

A题 纯质数(5分)

2021年第十二届蓝桥杯大赛软件赛决赛C/C++大学A组 个人部分题解_第1张图片

#include 
using namespace std;
const int N=20210605;
bool judge(int x)
{
     
    if(x==0||x==1)return 0;
    for(int i=2;i*i<=x;i++)
        if(x%i==0)return 0;
    return 1;
}
int main()
{
     
    ios::sync_with_stdio(false);
    int ans=0;
    for(int i=1;i<=N;i++)
    {
     
        if(judge(i))
        {
     
            int x=i;
            bool flag=1;
            while(x)
            {
     
                if(!judge(x%10)){
     flag=0;break;}
                x/=10;
            }
            if(flag)ans++;
        }
    }
    printf("%d\n",ans);
    return 0;
}

答案:1903

B题 完全日期(5分)

2021年第十二届蓝桥杯大赛软件赛决赛C/C++大学A组 个人部分题解_第2张图片

#include 
using namespace std;
const int N=1e6+10;
bool vis[N];
int get_day(int y,int m)
{
     
    if(m==2)
    {
     
        if(y%4==0&&y%100!=0)return 29; // 2020.02.29
        else return 28; // 2021.02.28
    }
    else
    {
     
        if(m==1||m==3||m==5||m==7||m==8||m==10||m==12)return 31;
        return 30;
    }
}
int f(int x)
{
     
    int s=0;
    while(x)
    {
     
        s+=x%10;
        x/=10;
    }
    return s;
}
int main()
{
     
    for(int i=1;i<=1000;i++)
        vis[i*i]=1;
    int ans=0;
    for(int y=2001;y<=2021;y++)
    {
     
        for(int m=1;m<=12;m++)
        {
     
            int allday=get_day(y,m);
            for(int day=1;day<=allday;day++)
            {
     
                int sum=f(y)+f(m)+f(day);
                if(vis[sum])ans++;
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

答案:977

C题 最小权值(10分)

2021年第十二届蓝桥杯大赛软件赛决赛C/C++大学A组 个人部分题解_第3张图片
不会…看到有人说猜一个完全二叉树的结论,然后记忆化搜索

更新:树形dp/记忆化搜索

D题 覆盖(10分)

2021年第十二届蓝桥杯大赛软件赛决赛C/C++大学A组 个人部分题解_第4张图片
不会

更新:状压dp

E题 123(15分)

试题 E: 123
时间限制: 1.0s 内存限制: 256.0MB 本题总分:15 分
【问题描述】
小蓝发现了一个有趣的数列,这个数列的前几项如下:
1, 1, 2, 1, 2, 3, 1, 2, 3, 4, ...
小蓝发现,这个数列前 1 项是整数 1,接下来 2 项是整数 12,接下来
3 项是整数 13,接下来 4 项是整数 14,依次类推。
小蓝想知道,这个数列中,连续一段的和是多少。
【输入格式】
输入的第一行包含一个整数 T,表示询问的个数。
接下来 T 行,每行包含一组询问,其中第 i 行包含两个整数 li 和 ri,表示
询问数列中第 li 个数到第 ri 个数的和。
【输出格式】
输出 T 行,每行包含一个整数表示对应询问的答案。
【样例输入】
3
1 1
1 3
5 8
【样例输出】
1
4
8
【评测用例规模与约定】
对于 10% 的评测用例,1 ≤ T ≤ 30, 1 ≤ li ≤ ri ≤ 100。
对于 20% 的评测用例,1 ≤ T ≤ 100, 1 ≤ li ≤ ri ≤ 1000。
对于 40% 的评测用例,1 ≤ T ≤ 1000, 1 ≤ li ≤ ri ≤ 10^6。
对于 70% 的评测用例,1 ≤ T ≤ 10000, 1 ≤ li ≤ ri ≤ 10^9。
对于 80% 的评测用例,1 ≤ T ≤ 1000, 1 ≤ li ≤ ri ≤ 10^12。
对于 90% 的评测用例,1 ≤ T ≤ 10000, 1 ≤ li ≤ ri ≤ 10^12。
对于所有评测用例,1 ≤ T ≤ 100000, 1 ≤ li ≤ ri ≤ 10^12

类似于分块写的,分成中间若干个完整组,然后加上左右两边的部分组。预处理一下前 i i i组个数和前缀和,用二分找其位置。

按100%数据写的,期望别写挂

#include 
using namespace std;
typedef unsigned long long ll;
const ll N=2e6+3;
ll a[N+10],sum[N+10];
ll get_id(ll x) // 得到组号
{
     
    return (ll)(lower_bound(a+1,a+N+1,x)-a);
}
int main()
{
     
    ios::sync_with_stdio(false);
    for(ll i=1;i<=N;i++)
    {
     
        a[i]=(1+i)*i/2; // 前i组个数
        sum[i]=sum[i-1]+a[i]; // 前i组的和
    }
    int T;cin>>T;
    ll l,r,ans;
    while(T--)
    {
     
        cin>>l>>r;
        ll n1=get_id(l);
        ll n2=get_id(r);
        if(n1==n2) // 同一组
        {
     
            ll num=r-l+1;
            ll x=l-a[n1-1];
            ans=x*num+num*(num-1)/2;
        }
        else
        {
     
            ll num=a[n1]-l+1;
            ll x=l-a[n1-1];
            ll s1=x*num+num*(num-1)/2; // 前面部分
            ll s2=sum[n2-1]-sum[n1]; // 中间的完整组
            num=r-a[n2-1];
            ll s3=num+num*(num-1)/2; // 最后部分
            ans=s1+s2+s3;
        }
        printf("%llu\n",ans);
    }
    return 0;
}
/*
999999999999 1000000000000
ans:176417
1000000000000 1000000000000
ans:88209
*/

F题 异或变换(15分)

试题 F: 异或变换
时间限制: 1.0s 内存限制: 256.0MB 本题总分:15 分
【问题描述】
小蓝有一个 01 串 s = s1 s2 s3 · · · sn。
以后每个时刻,小蓝要对这个 01 串进行一次变换。每次变换的规则相同。
对于 01 串 s = s1 s2 s3 · · · sn,变换后的 01 串 s′ = s1′s2′s3′· · · sn′为:
s1′ = s1;
si = si−1′ ⊕ si。
其中 a ⊕ b 表示两个二进制的异或,当 a 和 b 相同时结果为 0,当 a 和 b
不同时结果为 1。
请问,经过 t 次变换后的 01 串是什么?
【输入格式】
输入的第一行包含两个整数 n, t,分别表示 01 串的长度和变换的次数。
第二行包含一个长度为 n 的 01 串。
【输出格式】
输出一行包含一个 01 串,为变换后的串。
【样例输入】
5 3
10110
【样例输出】
11010
【样例说明】
初始时为 10110,变换 1 次后变为 11101,变换 2 次后变为 10011,变换 3
次后变为 11010。
【评测用例规模与约定】
对于 40% 的评测用例,1 ≤ n ≤ 100, 1 ≤ t ≤ 1000。
对于 80% 的评测用例,1 ≤ n ≤ 1000, 1 ≤ t ≤ 10^9。
对于所有评测用例,1 ≤ n ≤ 10000, 1 ≤ t ≤ 10^18

找循环节,若某个字符串第二次出现就停止循环。当然这么处理之后也过不了全部数据,估计40%左右

#include 
using namespace std;
typedef long long ll;
unordered_map<string,ll>vis;
unordered_map<ll,string>mp;
string f(string s)
{
     
    string res=s;
    for(int i=1;s[i];i++)
        res[i]=(s[i]-'0')^(s[i-1]-'0')+'0';
    return res;
}
int main()
{
     
    ios::sync_with_stdio(false);
    string s;
    int n;ll k;
    cin>>n>>k>>s;
    vis[s]=(ll)1;
    mp[(ll)1]=s;
    for(ll i=2;i<=k+1;i++) // k次
    {
     
        s=f(s);
        if(!vis[s])
        {
     
            vis[s]=i;
            mp[i]=s;
        }
        else // 找到循环节
        {
     
            ll pos=vis[s]; // 上一次出现的位置
            ll ansk=(k+1-pos)%(i-pos);
            s=mp[pos+ansk];
            break;
        }
    }
    printf("%s\n",s.c_str());
    return 0;
}
/*
5 1000000000000000000
10110
*/

G题 冰山(20分)

试题 G: 冰山
时间限制: 1.0s 内存限制: 256.0MB 本题总分:20 分
【问题描述】
一片海域上有一些冰山,第 i 座冰山的体积为 Vi。
随着气温的变化,冰山的体积可能增大或缩小。第 i 天,每座冰山的变化
量都是 Xi。当 Xi > 0 时,所有冰山体积增加 Xi;当 Xi < 0 时,所有冰山体积减
少 −Xi;当 Xi = 0 时,所有冰山体积不变。
如果第 i 天某座冰山的体积变化后小于等于 0,则冰山会永远消失。
冰山有大小限制 k。如果第 i 天某座冰山 j 的体积变化后 Vj 大于 k,则它
会分裂成一个体积为 k 的冰山和 Vj − k 座体积为 1 的冰山。
第 i 天结束前(冰山增大、缩小、消失、分裂完成后),会漂来一座体积为
Yi 的冰山(Yi = 0 表示没有冰山漂来)。
小蓝在连续的 m 天对这片海域进行了观察,并准确记录了冰山的变化。小
蓝想知道,每天结束时所有冰山的体积之和(包括新漂来的)是多少。
由于答案可能很大,请输出答案除以 998244353 的余数。
【输入格式】
输入的第一行包含三个整数 n, m, k,分别表示初始时冰山的数量、观察的
天数以及冰山的大小限制。
第二行包含 n 个整数 V1, V2, · · · , Vn,表示初始时每座冰山的体积。
接下来 m 行描述观察的 m 天的冰山变化。其中第 i 行包含两个整数 Xi
, Yi,意义如前所述。
【输出格式】
输出 m 行,每行包含一个整数,分别对应每天结束时所有冰山的体积之和
除以 998244353 的余数。
【样例输入】
1 3 6
1
6 1
2 2
-1 1
【样例输出】
8
16
11
【样例说明】
在本样例说明中,用 [a1, a2, · · · , an] 来表示每座冰山的体积。
初始时的冰山为 [1]。
第 1 天结束时,有 3 座冰山:[1, 1, 6]。
第 2 天结束时,有 6 座冰山:[1, 1, 2, 3, 3, 6]。
第 3 天结束时,有 5 座冰山:[1, 1, 2, 2, 5]。
【评测用例规模与约定】
对于 40% 的评测用例,n, m, k ≤ 2000;
对于 60% 的评测用例,n, m, k ≤ 20000;
对于所有评测用例,1 ≤ n, m ≤ 100000, 1 ≤ k ≤ 10^9, 1 ≤ Vi ≤ k, 0 ≤ Yi ≤ k, −k ≤ Xi ≤ k。

分成Vj − k个体积为1的冰山,会影响冰山个数和后续冰山的变化,这个地方没想到怎么优化处理…只写了暴力,水个40%数据

#include 
using namespace std;
typedef long long ll;
const ll N=1e5+10,mod=998244353;
vector<ll>g;
vector<ll> f(vector<ll> t,ll x,ll y,ll k)
{
     
    vector<ll>res;
    ll sz=(ll)t.size();
    for(ll i=0;i<sz;i++)
    {
     
        t[i]+=x;
        if(t[i]>0) // 只保留大于0的
        {
     
            if(t[i]<=k)res.push_back(t[i]);
            else // 分裂
            {
     
                res.push_back(k);
                for(ll j=1;j<=t[i]-k;j++) // 没想到这里怎么优化
                    res.push_back((ll)1);
            }
        }
    }
    if(y>0)res.push_back(y);
    return res;
}
int main()
{
     
    ios::sync_with_stdio(false);
    ll n,m,k,x,y;
    cin>>n>>m>>k;
    for(ll i=1;i<=n;i++)
    {
     
        cin>>x;
        g.push_back(x);
    }
    for(ll i=1;i<=m;i++)
    {
     
        cin>>x>>y;
        g=f(g,x,y,k);
        ll sum=0;
        for(ll j=0;j<g.size();j++)
            sum=(sum+g[j])%mod;
        printf("%lld\n",sum);
    }
    return 0;
}

H题 翻转括号序列(20分)

试题 H: 翻转括号序列
时间限制: 2.0s 内存限制: 512.0MB 本题总分:20 分
【问题描述】
给定一个长度为 n 的括号序列,要求支持两种操作:
1.[Li, Ri] 区间内(序列中的第 Li 个字符到第 Ri 个字符)的括号全部翻
转(左括号变成右括号,右括号变成左括号)。
2. 求出以 Li 为左端点时,最长的合法括号序列对应的 Ri (即找出最大的
Ri 使 [Li, Ri] 是一个合法括号序列)。
【输入格式】
输入的第一行包含两个整数 n, m,分别表示括号序列长度和操作次数。
第二行包含给定的括号序列,括号序列中只包含左括号和右括号。
接下来 m 行,每行描述一个操作。如果该行为 “1 Li Ri”,表示第一种操作,
区间为 [Li, Ri] ;如果该行为 “2 Li” 表示第二种操作,左端点为 Li。
【输出格式】
对于每个第二种操作,输出一行,表示对应的 Ri。如果不存在这样的 Ri,请输出 0。
【样例输入】
7 5
((())()
2 3
2 2
1 3 5
2 3
2 1
【样例输出】
4
7
0
0
【评测用例规模与约定】
对于 20% 的评测用例,n, m ≤ 5000;
对于 40% 的评测用例,n, m ≤ 30000;
对于 60% 的评测用例,n, m ≤ 100000;
对于所有评测用例,1 ≤ n ≤ 10^6, 1 ≤ m ≤ 2 × 10^5

这题看到有人说左右括号看成-1和1然后用树状数组维护找最大区间使其和为0然后再加限制条件保证左右能匹配上…然而我还是暴力,水一下20%数据

#include 
using namespace std;
const int N=1e6+10,inf=0x7f7f7f7f;
typedef long long ll;
int n,m;
char s[N];
int judge(char s[],int l)
{
     
    stack<char>g;
    while(g.size())g.pop();
    int ans=0;
    for(int i=l;i<=n;i++)
    {
     
        if(g.empty())g.push(s[i]);
        else
        {
     
            if(g.top()=='('&&s[i]==')')g.pop();
            else g.push(s[i]);
        }
        if(g.empty()) ans=i;
    }
    return ans;
}
int main()
{
     
    ios::sync_with_stdio(false);
    int opt,l,r;
    cin>>n>>m>>s+1;
    for(int i=1;i<=m;i++)
    {
     
        cin>>opt;
        if(opt==1)
        {
     
            cin>>l>>r;
            for(int i=l;i<=r;i++)
            {
     
                if(s[i]=='(')s[i]=')';
                else s[i]='(';
            }
        }
        else
        {
     
            cin>>l;
            printf("%d\n",judge(s,l));
        }
    }
    return 0;
}

I题 异或三角(25分)

试题 I: 异或三角
时间限制: 1.0s 内存限制: 256.0MB 本题总分:25 分
【问题描述】
给定 T 个数 n1, n2, · · · , nT,对每个 ni 请求出有多少组 a, b, c 满足:
1. 1 ≤ a, b, c ≤ ni;
2. a ⊕ b ⊕ c = 0,其中 ⊕ 表示二进制按位异或;
3. 长度为 a, b, c 的三条边能组成一个三角形。
【输入格式】
输入的第一行包含一个整数 T。
接下来 T 行每行一个整数,分别表示 n1, n2, · · · , nT。
【输出格式】
输出 T 行,每行包含一个整数,表示对应的答案。
【样例输入】
2
6
114514
【样例输出】
6
11223848130
【评测用例规模与约定】
对于 10% 的评测用例,T = 1, 1 ≤ ni ≤ 200;
对于 20% 的评测用例,T = 1, 1 ≤ ni ≤ 2000;
对于 50% 的评测用例,T = 1, 1 ≤ ni ≤ 2^20;
对于 60% 的评测用例,1 ≤ T ≤ 100000, 1 ≤ ni ≤ 2^20;
对于所有评测用例,1 ≤ T ≤ 100000, 1 ≤ ni ≤ 2^30

依然写个暴力,水一下20%数据

#include 
using namespace std;
typedef long long ll;
int main()
{
     
    ios::sync_with_stdio(false);
    int T;cin>>T;
    while(T--)
    {
     
        ll n;cin>>n;
        ll ans=0;
        for(ll i=1;i<=n;i++)
        {
     
            for(ll j=1;j<=n;j++)
            {
     
                ll k=i^j;
                if(k<=n&&k>=1&&i+j>k&&i+k>j&&k+j>i)ans++;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

J题 积木(25分)

试题 J: 积木
时间限制: 2.0s 内存限制: 256.0MB 本题总分:25 分
【问题描述】
小蓝有大量正方体的积木(所有积木完全相同),他准备用积木搭一个巨大
的图形。
小蓝将积木全部平铺在地面上,而不垒起来,以便更稳定。他将积木摆成
一行一行的,每行的左边对齐,形成最终的图形。
第一行小蓝摆了 H1 = w 块积木。从第二行开始,第 i 行的积木数量 Hi 都
至少比上一行多 L,至多比上一行多 R(当 L = 0 时表示可以和上一行的积木
数量相同),即 Hi−1 + L ≤ Hi ≤ Hi−1 + R。
给定 x, y 和 z,请问满足以上条件的方案中,有多少种方案满足第 y 行的
积木数量恰好为第 x 行的积木数量的 z 倍。
【输入格式】
输入一行包含 7 个整数 n, w, L, R, x, y, z,意义如上所述。
【输出格式】
输出一个整数, 表示满足条件的方案数,答案可能很大,请输出答案除以
998244353 的余数。
【样例输入 15 1 1 2 2 5 3
【样例输出 14
【样例输入 2233 5 1 8 100 215 3
【样例输出 2308810105
【评测用例规模与约定】
对于 10% 的评测用例,1 ≤ n ≤ 10, 1 ≤ w ≤ 10, 0 ≤ L ≤ R ≤ 3;
对于 20% 的评测用例,1 ≤ n ≤ 20, 1 ≤ w ≤ 10, 0 ≤ L ≤ R ≤ 4;
对于 35% 的评测用例,1 ≤ n ≤ 500, 0 ≤ L ≤ R ≤ 10;
对于 50% 的评测用例,1 ≤ n ≤ 5000, 0 ≤ L ≤ R ≤ 10;
对于 60% 的评测用例,1 ≤ n ≤ 20000, 0 ≤ L ≤ R ≤ 10;
对于 70% 的评测用例,1 ≤ n ≤ 50000, 0 ≤ L ≤ R ≤ 10;
对于 85% 的评测用例,1 ≤ n ≤ 300000, 0 ≤ L ≤ R ≤ 10;
对于所有评测用例,1 ≤ n ≤ 500000, 1 ≤ w ≤ 10^9, 
0 ≤ L ≤ R ≤ 40, 1 ≤ x < y ≤ n, 0 ≤ z ≤ 10^9

继续暴力,水个10%数据(连n=20都过不了,没时间写了)

#include 
using namespace std;
typedef long long ll;
const ll N=1e5+10,mod=998244353;
ll a[N],ans,n,w,l,r,x,y,z;
void dfs(ll i,ll u)
{
     
    a[i]=u;
    if(i==y)
    {
     
        if(a[i]==a[x]*z)ans++;
        return;
    }
    for(ll h=a[i]+l;h<=a[i]+r;h++)
        dfs(i+1,h);
}
int main()
{
     
    ios::sync_with_stdio(false);
    cin>>n>>w>>l>>r>>x>>y>>z;
    dfs(1,w);
    printf("%lld\n",ans%mod);
    return 0;
}
/*
10 1 0 4 1 10 2
ans: 9
*/

你可能感兴趣的:(算法,数据结构,蓝桥杯)