【大部分暴力】2021年第十二届蓝桥杯决赛 C++ B组

试题 A:带宽

【问题描述】

​ 小蓝家的网络带宽是 200 Mbps,请问,使用小蓝家的网络理论上每秒钟最多可以从网上下载多少MB的内容。

【答案】:25

很简单,没什么好说的。

#include <iostream>

using namespace std;

int main()
{
    cout<<200/8;
    return 0;
}


试题 B:纯质数

【问题描述】

​ 如果一个正整数只有 1 和它本身两个约数,则称为一个质数(又称素数)。

​ 前几个质数是:2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, …… 。

​ 如果一个质数的所有十进制数位都是质数,我们称它为纯质数。例如:2, 3, 5, 7, 23, 37 都是纯质数,而 11, 13, 17, 19, 29, 31 不是纯质数。当然1, 4, 35 也不是纯质数。

​ 请问,在 1 到 20210605 中,有多少个纯质数?

【答案】:1903

填空题,能跑出来就行。先检验每位是不是质数,再检验这个数是不是质数,会快一点。大概20秒也就跑完了。

#include <iostream>

using namespace std;

bool isZhiShu(int num)
{
    int temp = num;
    while(temp)
    {
        int t = temp%10;
        if(t!=2 && t!=3 && t!=5 && t!=7){return false;}
        temp /= 10;
    }

    for(int i=2;i<num;i++)
    {
        if(num%i==0){return false;}
    }
    
    return true;
}

int main()
{
    int result = 0;
    for(int i=2;i<=20210605;i++)
    {
        if(isZhiShu(i))
        {
            result++;
        }
    }
    cout<<result;
    return 0;
}


试题 C:完全日期

【问题描述】

​ 如果一个日期中年月日的各位数字之和是完全平方数,则称为一个完全日期。

​ 例如:2021 年 6 月 5 日的各位数字之和为 2 + 0 + 2 + 1 + 6 + 5 = 16 2+0+2+1+6+5=16 2+0+2+1+6+5=16 ,而 16 是一个完全平方数,它是 4 的平方。所以 2021 年 6 月 5 日是一个完全日期。

​ 请问, 从 2001 年 1 月 1 日到 2021 年 12 月 31 日中,一共有多少个完全日期?

【答案】:977

#include <iostream>
#include <unordered_map>

using namespace std;

int y, m, d;
int dom[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
unordered_map<int,int> c;

int sum(int num)
{
    int result = 0;
    while(num)
    {
        result += num%10;
        num /= 10;
    }
    return result;
}

int main()
{
    for(int i=0;i<10;i++){c[i*i]=1;}

    int result = 0;
    y=2001;m=1;d=1;

    while(true)
    {
        if(c[ sum(y)+sum(m)+sum(d) ]==1){result++;}

        if(y==2021 && m==12 && d==31){break;}

        if(++d>dom[m])
        {
            d=1;
            if(++m>12)
            {
                m = 1;
                y++;
                if((y%4==0 && y%100!=0)||y%400==0)
                    {dom[2]=29;}
                else
                    {dom[2]=28;}
            }
        }
    }

    cout<<result;
    return 0;
}


试题 D:最小权值

【问题描述】

​ 对于一棵有根二叉树 T T T,小蓝定义这棵树中结点的权值 W ( T ) W(T) W(T) 如下:

​ 空子树的权值为 0 0 0

​ 如果一个节点 v v v 有左子树 L L L ,右子树 R R R ,分别有 C ( L ) C(L) C(L) C ( R ) C(R) C(R) 个结点,则 W ( v ) = 1 + 2 W ( L ) + 3 W ( R ) + ( C ( L ) ) 2 C ( R ) W(v) = 1+2W(L)+3W(R)+(C(L))^2C(R) W(v)=1+2W(L)+3W(R)+(C(L))2C(R)

​ 树的权值定义为树的根结点的权值。

​ 小蓝想知道,对于一棵有 2021 个节点的二叉树,树的权值最小可能是多少?

【答案】:2653631372

一眼dp。

#include <iostream>
#include <unordered_map>

using namespace std;
unordered_map<int,int> visited;
unordered_map<int,long long int> dpmap;

long long int dp(int num)
{
    if(num==0){return 0;}
    if(visited[num]==1){return dpmap[num];}

    long long int result = 1e10;
    for(int i=0;i<num;i++)
    {
        long long int w = 1 + 2*dp(i) + 3*dp(num-i-1) + i*i*(num-i-1);
        if(w<result){result = w;}
    }
    visited[num] = 1;
    dpmap[num] = result;
    return result;
}

int main()
{
    cout<<dp(2021);
    return 0;
}


试题 E:大写

【问题描述】

​ 给定一个只包含大写字母和小写字母的字符串,请将其中所有小写字母转换成大写字母后将字符串输出。

【输入格式】

​ 输入一行包含一个字符串。

【输出格式】

​ 输出转换成大写后的字符串。

【答案】

#include <iostream>

using namespace std;

int main()
{
    string s;
    cin>>s;

    for(int i=0;i<s.size();i++)
    {
        if(s[i]>='a' && s[i]<='z')
        {
            s[i] = s[i] - 'a' + 'A';
        }
    }

    cout<<s;
    return 0;
}


试题 F:123

【问题描述】

​ 小蓝发现了一个有趣的数列,这个数列的前几项如下:

1 , 1 , 2 , 1 , 2 , 3 , 1 , 2 , 3 , 4 , … … 1,1,2,1,2,3,1,2,3,4,…… 1,1,2,1,2,3,1,2,3,4,

​ 小蓝发现,这个数列前 1 项是整数 1 ,接下来 2 项是整数 1 至 2 ,接下来 3 项是整数 1 至 3 ,接下来 4 项是整数 1 至 4 ,依次类推。

​ 小蓝想知道,这个数列中,连续一段的和是多少。

【输入格式】

​ 输入的第一行包含一个整数 T T T ,表示询问的个数。

​ 接下来 T T T 行,每行包含一组询问,其中第 i i i 行包含两个整数 l i l_i li r i r_i ri ,表示询问数列中第 l i l_i li 个数到第 r i r_i ri 个数的和。

【输出格式】

​ 输出 T T T 行,每行包含一个整数表示对应询问的答案。

【答案】

自然数列求和是吧,我先放个自然数列求和公式在这: S ( n ) = ( 1 + n ) n 2 S(n) = \frac {(1+n)n}{2} S(n)=2(1+n)n , 那第 s s s 项到第 e e e 项求和公式就是 $ S(e) - S(s-1) = \frac {(s+e)(e-s+1)}{2}$

然后想到分组。可以将数列第 n n n 个数转换为:数列第 k n k_n kn 组,第 j n j_n jn 个数。每一组中数字个数与组号 k k k 相同。

其中,组号 k k k 可以通过求解一元二次方程 $\frac {(1+k)*k} 2 = n $ 再向上取整得到,用求根公式就行。(我因为比赛前刚考了门金融数值计算,然后我惯性地写了个牛顿迭代,出来之后才想起来,笑死)当然,要取正的那个。 k = ⌈ − 1 + 1 + 8 n 2 ⌉ k = \lceil \frac {-1+ \sqrt {1+8n}} {2} \rceil k=21+1+8n ,所以 j = n − ( k − 1 ) k 2 j = n - \frac {(k-1)k}{2} j=n2(k1)k

然后就可以写出数列求和的公式了 。

S ( n ) = { ∑ r = 1 j r k = 1 ∑ r = 1 k − 1 ∑ m = 1 r m + ∑ r = 1 j r k > 1 S\left( n \right) =\begin{cases} \sum_{r=1}^j{r}& k=1\\ \sum_{r=1}^{k-1}{\sum_{m=1}^r{m}}+\sum_{r=1}^j{r}& k>1\\ \end{cases} S(n)={r=1jrr=1k1m=1rm+r=1jrk=1k>1

接下来就看 k > 1 k > 1 k>1 的情况了

带入求和公式之后就是:

S ( n ) = ∑ k = 1 k − 1 ( 1 + k ) k 2 + ( 1 + j ) j 2 S(n) = \sum_{k=1}^{k-1} \frac {(1+k)k}{2} + \frac {(1+j)j}{2} S(n)=k=1k12(1+k)k+2(1+j)j

然后中间其实是一个等差数列和一个平方数求和,于是进一步带公式 1 2 + 2 2 + 3 2 + ⋯ + n 2 = n ( n + 1 ) ( 2 n + 1 ) 6 1^2+2^2+3^2+ \cdots +n^2 = \frac{n(n+1)(2n+1)}{6} 12+22+32++n2=6n(n+1)(2n+1)

S ( n ) = ( k − 1 ) k ( 2 k − 1 ) 12 + k ( k − 1 ) 4 + ( 1 + j ) j 2 S(n) = \frac {(k-1)k(2k-1)}{12} + \frac {k(k-1)}{4} + \frac {(1+j)j}{2} S(n)=12(k1)k(2k1)+4k(k1)+2(1+j)j

最后只要求 S ( r ) − S ( l − 1 ) S(r)-S(l-1) S(r)S(l1) 即可。

但我平方和公式现场做的时候没想起来,只能过部分用例了。

#include <iostream>
#include <math.h>

using namespace std;

long long int solvek(long long int num)
{
    float n = num;

    return ceil((-1+sqrt(1 + 8*n))/2);
}

long long int solvej(long long int n, long long int k)
{
    return n - ((k-1)*k)/2;
}

long long int sumn(long long int e)
{
    return (1+e)*e/2;
}

long long int sums(long long int n)
{
    return n*(n+1)*(2*n+1)/6;
}

long long int solve(long long int n)
{
    long long int k = solvek(n);
    long long int j = solvej(n, k);

    return (sums(k-1)+sumn(k-1))/2 +  sumn(j);

}

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        long long int l, r;
        cin>>l>>r;
        cout<<solve(r)-solve(l-1)<<endl;
    }
    return 0;
}


试题 G:异或变换

【问题描述】

​ 小蓝有一个 01 串 s = s 1 s 2 s 3 ⋯ s n s=s_1s_2s_3 \cdots s_n s=s1s2s3sn

​ 以后每个时刻,小蓝要对这个 01 串进行一次变换。每次变换的规则相同。

​ 对于 01 串 s = s 1 s 2 s 3 ⋯ s n s=s_1s_2s_3 \cdots s_n s=s1s2s3sn ,变换后的 01 串 s ′ = s 1 ′ s 2 ′ s 3 ′ ⋯ s n ′ s^{'}=s^{'}_1s^{'}_2s^{'}_3 \cdots s^{'}_n s=s1s2s3sn 为:

s 1 ′ = s 1 s^{'}_1 = s_1 s1=s1

s i ′ = s i − 1 ⊕ s i s^{'}_i = s_{i-1} \oplus s_i si=si1si

​ 其中 a ⊕ b a \oplus b ab 表示两个二进制的异或,当 a a a b b b 相同时结果为 0,当 a a a b b b 不同时结果为 1。

​ 请问,经过 t t t 次变换后的 01 串是什么?

【输入格式】

​ 输入的第一行包含两个整数 n , t n,t n,t ,分别表示 01 串的长度和变换的次数。

​ 第二行包含一个长度为 n n n 的 01 串。

【输出格式】

​ 输出一行包含一个 01 串,为变换后的串。

【答案】

不会,暴力了。

#include <iostream>

using namespace std;

int main()
{
    string s;
    int n,t;
    cin>>n>>t;
    cin>>s;
    while(t--)
    {
        string t = s;
        for(int i=1;i<n;i++)
        {
            if(s[i-1]==s[i])
                {t[i]='0';}
            else
                {t[i]='1';}
        }
        if(t==s){break;}
        s = t;
    }
    cout<<s<<endl;
    return 0;
}


试题 H:二进制问题

【问题描述】

​ 小蓝最近在学习二进制。他想知道 1 到 N N N 中有多少个数满足其二进制表示中恰好有 K K K 个 1 。你能帮助他吗?

【输入格式】

​ 输入一行包含两个整数 N N N K K K

【输出格式】

​ 输出一个整数表示答案。

【答案】

对不起,小蓝。我有一个绝妙的想法,可惜时间不够了。暴力了。

#include <iostream>

using namespace std;

int n,k;

int mypow(int n, int k)
{
    if(k==1){return n;}
    int temp = mypow(n, k/2);

    if(k%2==1)
        {return temp*temp*n;}
    else
        {return temp*temp;}
}

bool isTrue(int num)
{
    int n = 0;
    while(num)
    {
        if(num%2==1)
        {
            if(++n>k){return false;}
        }
        num /= 2;
    }
    if(n!=k){return false;}
    return true;
}

int main()
{
    int result=0;
    cin>>n>>k;
    for(int i=mypow(2,k)-1;i<=n;i++)
    {
        if(isTrue(i))
        {
            result++;
        }
    }
    cout<<result<<endl;
    return 0;
}


试题 I:翻转括号序列

【问题描述】

​ 给定一个长度为 n n n 的括号序列,要求支持两种操作;

​ 1.将 [ L i , R i ] [L_i,R_i] [Li,Ri] 区间内(序列中的第 L i L_i Li 个字符到第 R i R_i Ri 个字符)的括号全部翻转(左括号变成右括号,右括号变成左括号)。

​ 2.求出以 L i L_i Li 为左端点时,最长的合法括号序列对应的 R i R_i Ri (既找出最大的 R i R_i Ri 使 [ L i , R i ] [L_i,R_i] [Li,Ri] 是一个合法括号序列。

【输入格式】

​ 输入的第一行包含两个整数 n , m n,m n,m ,分别表示括号序列长度和操作次数。

​ 第二行包含给定的括号序列,括号序列中只包含左括号和右括号。

​ 接下来 m m m 行,每行描述一个操作。如果该行为“1 L i R i L_i R_i LiRi”,表示第一种操作,区间为 [ L i , R i ] [L_i,R_i] [Li,Ri];如果该行为“2 L i L_i Li”表示第二种操作,左端点为 L i L_i Li

【输出格式】

​ 对于每个第二种操作,输出一行,表示对应的 R i R_i Ri。如果不存在这样的 R i R_i Ri,请输出 0。

【答案】

一眼线段树,括号用01串替换。诶(éi),但是我不会。日常暴力。

#include <iostream>
#include <string>

using namespace std;

int n,m;
string s;

void change(int l, int r)
{
    for(int i=l-1;i<r;i++)
    {
        if(s[i]=='(')
            {s[i]=')';}
        else
            {s[i]='(';}
    }
    return;
}

int solve(int l)
{
    int lc = 0;
    int result = 0;

    for(int i=l-1;i<n;i++)
    {
        if(s[i]=='(')
            {lc++;}
        else
        {
            lc--;
            if(lc<0){break;}
        }
        if(lc==0){result=i+1;}
    }
    return result;
}

int main()
{
    cin>>n>>m;
    cin>>s;
    while(m--)
    {
        int type;
        cin>>type;
        if(type==1)
        {
            int l, r;
            cin>>l>>r;
            change(l, r);
        }
        else
        {
            int l;
            cin>>l;
            cout<<solve(l);
        }
    }
    return 0;
}


试题 J:异或三角

【问题描述】

​ 给定 T T T 个数 n 1 , n 2 , ⋯   , n T n_1,n_2,\cdots,n_T n1,n2,,nT ,对每个 n i n_i ni 请求出有多少组 a , b , c a,b,c a,b,c 满足:

       1. $1\le a,b,c \le n_i$;
       2.  $a \oplus b \oplus c = 0$ ,其中 $\oplus$ 表示二进制按位异或;
       3. 长度为 $a,b,c$ 的三条边能组成一个三角形。

【输入格式】

​ 输入的第一行包含一个整数 T T T

​ 接下来 T T T 行每行一个整数, 分别表示 n 1 , n 2 , ⋯   , n T n_1,n_2,\cdots,n_T n1,n2,,nT

【输出格式】

​ 输出 T T T 行,每行包含一个整数,表示对应的答案。

【答案】

不会,随便写了个dp,114514都没过(笑死)。

#include <iostream>
#include <unordered_map>

using namespace std;

unordered_map<int,int> visited;
unordered_map<int,int> dpmap;

int dp(int n)
{
    if(n==1){return 0;}
    if(visited[n]==1){return dpmap[n];}

    int result = dp(n-1);
    for(int i=n/2;i<n;i++)
    {
        for(int j=i-1;j>0;j--)
        {
            if(i+j<=n){continue;}
            if((i^j^n) == 0){result++;}
        }
    }
    visited[n] = 1;
    dpmap[n] = result;
    return result;
}

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        cout<<dp(n);
    }
    return 0;
}


【总结】

数学与应用数学专业来玩一玩,基本上啥也不会,结果居然是国二,这得奖率还是高的呀。可惜今年不在北京,也没个颁奖典礼啥的,可惜了。然后,比赛的时候没带吃的,但旁边的同学带了一大袋,不时吃一口,给我整饿了。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xuelanghanbao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值