2021年4月 第十二届蓝桥杯软件类省赛C++B组第一场 真题题解

题目结构

题目类型分值
第一题结果填空5分
第二题结果填空5分
第三题结果填空10分
第四题结果填空10分
第五题结果填空15分
第六题程序设计15分
第七题程序设计20分
第八题程序设计20分
第九题程序设计25分
第十题程序设计25分

填空题

第一题 空间

  • 问题描述

    小蓝准备用 256 M B 256MB 256MB 的内存空间开一个数组,数组的每个元素都是 32 32 32 位二进制整数,如果不考虑程序占用的空间和维护内存需要的辅助空间,请问 256 M B 256MB 256MB 的空间可以存储多少个 32 32 32 位二进制整数?

  • 解题思路

    1 M B = 2 10 K B = 2 20 B = 2 20 × 8 b i t 1MB=2^{10}KB=2^{20}B=2^{20}\times 8 bit 1MB=210KB=220B=220×8bit。换算输出即可。

  • 答案

    67108864 67108864 67108864


第二题 卡片

  • 问题描述

    小蓝有很多数字卡片,每张卡片上都是数字 0 0 0 9 9 9。小蓝准备用这些卡片来拼一些数,他想从 1 开始拼出正整数,每拼一个,就保存起来,卡片就不能用来拼其它数了。小蓝想知道自己能从 1 拼到多少。例如,当小蓝有 30 30 30 张卡片,其中 0 0 0 9 9 9 3 3 3 张,则小蓝可以拼出 1 到 10,但是拼 11 时卡片 1 已经只有一张了,不够拼出 11。现在小蓝手里有 0 到 9 的卡片各 2021 2021 2021 张,共 20210 20210 20210 张,请问小蓝可以从 1 1 1拼到多少?提示:建议使用计算机编程解决问题。

  • 解题思路

    直接模拟到用完为止,注意细节,最后退出的数需要减 1 1 1,因为我们拼不成。

  • 代码

/**
  *@filename:B
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-18 14:37
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;

int n;
int a[10];
bool work(int x){
    while(x){
        int temp=x%10;
        x/=10;
        if(a[temp]>0){
            a[temp]--;
        }
        else{
            return false;
        }
    }
    return true;
}
int main(){
    while(cin>>n){
        for(int i=0;i<=9;i++)a[i]=n;
        int cnt=1;
        while(true){
            if(work(cnt)){
                cnt++;
            }
            else{
                break;
            }
        }
        cout<<cnt-1<<endl;//注意减1.
        //3181
    }
    return 0;
}
  • 答案

    3181 3181 3181


第三题 直线

  • 问题描述

    在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,那么这些点中任意两点确定的直线是同一条。给定平面上 2 × 3 2 × 3 2×3个整点 ( x , y ) ∣ 0 ≤ x < 2 , 0 ≤ y < 3 , x ∈ Z , y ∈ Z {(x, y)|0 ≤ x < 2, 0 ≤ y < 3, x∈ Z, y ∈ Z} (x,y)0x<2,0y<3,xZ,yZ,即横坐标是 0 到 1 (包含 0 和 1) 之间的整数、纵坐标是 0 0 0 2 2 2 (包含 0 0 0 2 2 2) 之间的整数的点。这些点一共确定了 11 条不同的直线。给定平面上 20 × 21 个整点 ( x , y ) ∣ 0 ≤ x < 20 , 0 ≤ y < 21 , x ∈ Z , y ∈ Z {(x, y)|0 ≤ x < 20, 0 ≤ y < 21, x ∈ Z, y ∈ Z} (x,y)0x<20,0y<21,xZ,yZ,即横坐标是 0 0 0 19 19 19 (包含 0 0 0 19 19 19) 之间的整数、纵坐标是 0 到 20 (包含 0 0 0 20 20 20) 之间的整数的点。请问这些点一共确定了多少条不同的直线。

  • 解题思路

    写错了,以为是找规律,比赛的时候推的公式为 n + m + ( n − 1 ) ∗ n / 2 ∗ ( m − 1 ) ∗ m / 2 ∗ 2 n+m+(n-1)*n/2*(m-1)*m/2*2 n+m+(n1)n/2(m1)m/22正确解法利用直线判重解决这道题,表示直线的方式有很多种,这里采用斜截式来表示,通set容器来判重。需要注意的一个细节就是需要避免 k = 0 k=0 k=0 k = i n f k=inf k=inf的情况。 具体看代码。

  • 代码

/**
  *@filename:C
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-18 14:40
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;

int n,m;
struct Point{
    int x,y;
    Point(){}
    Point(int x,int y){
        this->x=x,this->y=y;
    }
};
struct Line{
    //我们通过点斜式来确定一条直线,即y=kx+b;那么判重的方法就是判断k和b是不是相等的。
    double k,b;
    Line(){}
    Line(double k,double b){
        this->k=k,this->b=b;
    }
    //由于我们要利用set判重,故我们需要对<运算符重载。
    bool operator<(const Line &A)const {
        if(k==A.k)return b<A.b;
        return k<A.k;
    }
};
set<Line> t;
Point points[maxn];
int cnt=0;//点的个数。
void addLine(Point a1,Point a2){
    if(a1.x==a2.x||a1.y==a2.y)return;//对于k=0,或者k=无穷的情况我们不考虑,避免精度损失等问题。最后我们累加上这些即可。n+m。
    double temp=(a2.x-a1.x)*1.0;
    double k=(a2.y-a1.y)*1.0/temp;
    double b=(a1.y*a2.x-a1.x*a2.y)*1.0/temp;
    t.insert(Line(k,b));
}
int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            points[cnt++]=Point(i,j);
        }
    }
    //接下来确定直线加入set中。
    for(int i=0;i<cnt;i++){
        for(int j=0;j<cnt;j++){
            addLine(points[i],points[j]);
        }
    }
    cout<<t.size()+n+m<<endl;
    return 0;
}
  • 答案

    40257 40257 40257

第四题 货物摆放

  • 问题描述

    小蓝有一个超大的仓库,可以摆放很多货物。现在,小蓝有 n 箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、宽、高。小蓝希望所有的货物最终摆成一个大的立方体。即在长、宽、高的方向上分别堆 L 、 W 、 H L、W、H LWH 的货物,满足 n = L × W × H n = L × W × H n=L×W×H。给定 n,请问有多少种堆放货物的方案满足要求。例如,当 n = 4 n = 4 n=4 时,有以下 6 种方案: 1 × 1 × 4 、 1 × 2 × 2 、 1 × 4 × 1 、 2 × 1 × 2 、 2 × 2 × 1 、 4 × 1 × 1 1×1×4、1×2×2、1×4×1、2×1×2、 2 × 2 × 1、4 × 1 × 1 1×1×41×2×21×4×12×1×22×2×14×1×1。请问,当 n = 2021041820210418 n = 2021041820210418 n=2021041820210418(注意有 16 16 16 位数字)时,总共有多少种方案?提示:建议使用计算机编程解决问题。

  • 解题思路

    枚举因子,由于不同顺序是不同的方案,所以我们先要假定 i ≤ j ≤ k i\leq j \leq k ijk,然后最后我们就可以对 i , j , k i,j,k i,j,k排列了,这样所有的情况都会被考虑,而因为都不相同的时候是 6 6 6种排列,有且只有两个相同的时候是 3 3 3种排列,都相同的时候是一种排列。所以我们需要统计满足条件的数目 c n t 0 cnt0 cnt0、满足条件的数目且其中有且仅有两个相同的数目 c n t 1 cnt1 cnt1、满足条件的数目有且仅有三个相同的数目 c n t 2 cnt2 cnt2。最后答案即是 c n t 0 × 6 − c n t 1 × 3 − c n t 2 × 5 cnt0\times 6-cnt1\times 3 - cnt2\times 5 cnt0×6cnt1×3cnt2×5

  • 代码

/**
  *@filename:B
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-18 14:28
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;

ll n;
void solve(){
}
int main(){
    while(cin>>n){
        int cnt0=0,cnt1=0,cnt2=0;//cnt0统计所有情况,cnt1统计两个数相等的情况,cnt2统计三个数相等的情况
        for(ll i=1;i*i*i<=n;i++){
            if(n%i)continue;
            for(ll j=i;i*j*j<=n;j++){
                if(n%j)continue;
                ll k=n/(i*j);
                if(i*j*k==n){
                    cnt0++;
                    if(i==j&&j==k){
                        cnt2++;
                    }
                    else if(i==j||j==k||i==k){
                        cnt1++;
                    }
                }
            }
        }
        cout<<cnt0<<" "<<cnt1<<" "<<cnt2<<endl;//406 2 0
        //不相等6种排列,只有两个相等三种排列,三个相等一种排列。
        cout<<cnt0*6-cnt1*3-cnt2*5<<endl;//2430
    }
    solve();
    return 0;
}
  • 答案

    2430 2430 2430


第五题 路径

  • 问题描述

    小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。小蓝的图由 2021 2021 2021 个结点组成,依次编号 1 1 1 2021 2021 2021。对于两个不同的结点 a , b a, b a,b,如果 a a a b b b 的差的绝对值大于 21 21 21,则两个结点之间没有边相连;如果 a a a b b b 的差的绝对值小于等于 21 21 21,则两个点之间有一条长度为 a a a b b b 的最小公倍数的无向边相连。例如:结点 1 1 1 和结点 23 23 23 之间没有边相连;结点 3 3 3 和结点 24 24 24 之间有一条无向边,长度为 24 24 24;结点 15 15 15 和结点 25 25 25 之间有一条无向边,长度为 75 75 75。请计算,结点 1 1 1 和结点 2021 2021 2021 之间的最短路径长度是多少。提示:建议使用计算机编程解决问题。

  • 解题思路

    最短路模板题。本题我采用 d i j k s t r a dijkstra dijkstra算法实现的。

  • 代码

/**
  *@filename:E
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-18 14:50
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 10000 + 5;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;

//最短路板子题。
int n;
int g[maxn][maxn];
int dist[maxn];
bool vis[maxn];
int gcd(int n,int m){
    return n%m?gcd(m,n%m):m;
}
void init(){
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++){
        for(int j=1;j<=i;j++){
            if(abs(i-j)<=21){
                g[i][j]=g[j][i]=i*j/gcd(i,j);
            }
            else{
                g[i][j]=g[j][i]=inf;
            }
        }
    }
}
void dijkstra(int st){
    for(int i=1;i<=n;i++){
        dist[i]=g[st][i];
    }
    vis[st]=true;
    for(int i=1;i<=n;i++){
        int minn=inf,index;
        for(int j=1;j<=n;j++){
            if(!vis[j]&&dist[j]<minn){
                minn=dist[j];
                index=j;
            }
        }
        if(minn==inf)break;
        vis[index]=true;
        for(int j=1;j<=n;j++){
            if(!vis[j]&&dist[j]>dist[index]+g[index][j]){
                dist[j]=dist[index]+g[index][j];
            }
        }
    }
    cout<<dist[n]<<endl;//n=2021 print:10266837
}
void solve(){
    init();
    dijkstra(1);
}
int main(){
    while(cin>>n){
        solve();
    }
    return 0;
}
  • 答案

    10266837 10266837 10266837

程序题

第六题 时间显示

  • 问题描述

    小蓝要和朋友合作开发一个时间显示的网站。在服务器上,朋友已经获取了当前的时间,用一个整数表示,值为从 1970 1970 1970 1 1 1 1 1 1 00 : 00 : 00 00:00:00 00:00:00到当前时刻经过的毫秒数。现在,小蓝要在客户端显示出这个时间。小蓝不用显示出年月日,只需要显示出时分秒即可,毫秒也不用显示,直接舍去即可。给定一个用整数表示的时间,请将这个时间对应的时分秒输出。

    【输入格式】

    输入一行包含一个整数,表示时间。

    【输出格式】

    输出时分秒表示的当前时间,格式形如 H H : M M : S S HH:MM:SS HH:MM:SS,其中 H H HH HH 表示时,值为 0 0 0 23 23 23 M M MM MM 表示分,值为 0 0 0 59 59 59 S S SS SS 表示秒,值为 0 0 0 59 59 59。时、分、秒

    不足两位时补前导 0 0 0

    【样例输入 1】

    46800999 46800999 46800999

    【样例输出 1】

    13 : 00 : 00 13:00:00 13:00:00

    【样例输入 2】

    1618708103123 1618708103123 1618708103123

    【样例输出 2】

    01 : 08 : 23 01:08:23 01:08:23

    【评测用例规模与约定】

    对于所有评测用例,给定的时间为不超过 1018 的正整数。

  • 解题思路

    水题,转换时间。注意: 1 s = 1000 m s 1s=1000ms 1s=1000ms

  • AC代码

/**
  *@filename:F
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-18 14:57
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;

ll n;
void solve(){
    n/=1000;
    printf("%02d:%02d:%02d\n",(n%(24*3600))/3600,n%3600/60,n%60);
}
int main(){
    while(cin>>n){
        solve();
    }
    return 0;
}

第七题 砝码称重

  • 问题描述

    你有一架天平和 N N N 个砝码,这 N N N个砝码重量依次是 W 1 , W 2 , ⋅ ⋅ ⋅ , W N W_1, W_2, · · · , W_N W1,W2,,WN。请你计算一共可以称出多少种不同的重量?注意砝码可以放在天平两边。

    【输入格式】

    输入的第一行包含一个整数 N N N

    第二行包含 N N N 个整数: W 1 , W 2 , W 3 , ⋅ ⋅ ⋅ , W N W_1, W_2, W_3, · · · , W_N W1,W2,W3,,WN

    【输出格式】

    输出一个整数代表答案。

    【样例输入】

    3

    1 4 6

    【样例输出】

    10

    【样例说明】

    能称出的 10 种重量是:1、2、3、4、5、6、7、9、10、11。

    1 = 1;

    2 = 6 − 4 (天平一边放 6,另一边放 4);

    3 = 4 − 1;

    4 = 4;

    5 = 6 − 1;

    6 = 6;

    7 = 1 + 6;

    9 = 4 + 6 − 1;

    10 = 4 + 6;

    11 = 1 + 4 + 6。

    【评测用例规模与约定】

    对于 50% 的评测用例, 1 ≤ N ≤ 15 1 ≤ N≤ 15 1N15

    对于所有评测用例, 1 ≤ N ≤ 100 1 ≤ N ≤ 100 1N100 N N N 个砝码总重不超过 100000 100000 100000

  • 解题思路

    我们可以先来定义一下,我们总认为称出的重量为左边减右边,也就是说如果要增加就放左边,如果要减少就放右边,否则不放。

    这种决策问题一看就是 d p dp dp了,然而比赛的时候也还是不知道该怎么处理,于是用dfs枚举。能过一半的样例。
    补题:看了大佬的写法,确实是背包问题的变种,三种决策,不过这个题我们不是用 m a x max max来判断,因为我们只关心能否凑成,所以只需要累加之前的状态即可。

  • d f s dfs dfs捡分代码

/**
  *@filename:G
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-18 15:15
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;

//
int n;
int w[maxn];
int cnt[maxn];
int sum;
void dfs(int step,int ans){
    //step表示当前正在选择的砝码,ans为左边减右边的重量。
    if(step==n+1){
        //说明前n个已经选完了。
        if(ans<=0)return;
        cnt[ans]++;
        return;
    }
    dfs(step+1,ans+w[step]);//放左边。
    dfs(step+1,ans);//不放。
    dfs(step+1,ans-w[step]);//放右边。
}
void solve(){
    dfs(1,0);
    int ans=0;
    for(int i=1;i<=sum;i++){
        if(cnt[i]>0)ans++;
    }
    cout<<ans<<endl;
}
int main(){
    cin>>n;
    sum=0;
    for(int i=1;i<=n;i++){
        cin>>w[i];
        sum+=w[i];
    }
    solve();
    return 0;
}
  • 动态规划解题代码

/**
  *@filename:G砝码称重背包问题变种
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-19 18:55
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;

//背包dp。累加三种方案数即可。
int n;
int w[105];
int dp[105][maxn<<2];//dp[i][j]表示的即是前i个砝码中能凑成重量j的方案数。
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>w[i];
	//由于我们在进行决策的时候会出现负数,所以我们需要将数组进行偏移一个maxn确保枚举的时候不会出现错误。
	dp[0][maxn]=1;//注意设立起始值作为更新点。
	for(int i=1;i<=n;i++){
		for(int j=-maxn+w[i];j<=maxn;j++){
			//从小到大开始枚举。
			//不选,加上,减去三种决策。
			dp[i][j+maxn]=dp[i-1][j+maxn]+dp[i-1][j+maxn+w[i]]+dp[i-1][j+maxn-w[i]];
		}
	}
	int ans=0;//累加所有出现的状态。
	for(int j=maxn+1;j<=maxn*2;j++){
		if(dp[n][j])ans++;
	}
	cout<<ans<<endl;
	return 0;
}

第八题 杨辉三角形

  • 问题描述

    下面的图形是著名的杨辉三角形:

    在这里插入图片描述

    如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列:

    1 , 1 , 1 , 1 , 2 , 1 , 1 , 3 , 3 , 1 , 1 , 4 , 6 , 4 , 1 , . . . 1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, ... 1,1,1,1,2,1,1,3,3,1,1,4,6,4,1,...

    给定一个正整数 N N N,请你输出数列中第一次出现 N N N 是在第几个数?

    【输入格式】

    输入一个整数 N 。 N。 N

    【输出格式】

    输出一个整数代表答案。

    【样例输入】

    6

    【样例输出】

    13

    【评测用例规模与约定】

    对于 20% 的评测用例, 1 ≤ N ≤ 10 1 ≤ N≤ 10 1N10

    对于所有评测用例, 1 ≤ N ≤ 1000000000 1 ≤N ≤ 1000000000 1N1000000000

  • 解题思路

    杨辉三角第 i i i行第 j j j列的数为 C i − 1 j − 1 C_{i-1}^{j-1} Ci1j1,那么我们直接枚举 i , j i,j i,j即可。不过有一个坑点,比赛的时候没想到,就是 C n 1 C_{n}^1 Cn1的时候,需要枚举 O ( n 2 ) O(n^2) O(n2),没有想到。哎,只能过的 20 % 20\% 20%的分。

  • 偷分代码

/**
  *@filename:H
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-18 15:00
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;

ll C(ll n,ll m){
    ll ans=1;
    for(ll i=n,j=1;i>=(n-m+1);i--,j++){
        ans=ans*i/j;
    }
    return ans;
}
ll n;
void solve(){
    if(n==1){
        cout<<1<<endl;
        return;
    }
    bool flag=false;
    for(ll i=3;;i++){
        for(ll j=1;j<i;j++){
            if(C(i-1,j)==n){
                flag=true;
                cout<<(i-1)*i/2+j+1<<endl;//不过好像没考虑n特别大的时候。。。即跑1e9*1e9跑不出。
                break;
            }
        }
        if(flag)break;
    }
}
int main(){
    while(cin>>n){
        solve();
    }
    return 0;
}
  • 标程
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int n;
LL C(int a, int b)   //计算C(a,b)
{
    
    LL res = 1;
    for(int i = a, j = 1; j <= b; i --, j ++)
    {
    
        res = res * i / j;
        if(res > n)
            return res;     // 大于n已无意义,且防止爆LL
    }
    return res;
}
bool check(int k)
{
    
    // 二分该斜行,找到大于等于该值的第一个数
    // 左边界2k,右边界为max(l, n)取二者最大,避免右边界小于左边界
    int l = 2 * k, r = max(n,l);
    while(l < r)
    {
    
        int mid = l + r >> 1;
        if(C(mid, k) >= n) r = mid;
        else l = mid + 1;
    }
    if(C(r, k) != n)
        return false;
    cout << 1ll*(r + 1) * r / 2 + k + 1 << endl;
    return true;
}
int main()
{
    
    cin >> n;
    // 从第16斜行枚举
    for(int i = 16; ; i--)
        if(check(i))
            break;
    return 0;
}

第九题 双向排序

  • 问题描述

    给定序列 ( a 1 , a 2 , ⋅ ⋅ ⋅ , a n ) = ( 1 , 2 , ⋅ ⋅ ⋅ , n ) a_1, a_2, · · · , a_n) = (1, 2, · · · , n) a1,a2,,an)=(1,2,,n),即 a i = i a_i = i ai=i。小蓝将对这个序列进行 m m m 次操作,每次可能是将 a 1 , a 2 , ⋅ ⋅ ⋅ , a q i a_1, a_2, · · · , a_{q_i} a1,a2,,aqi 降序排列,或者将 a q i , a q i + 1 , ⋅ ⋅ ⋅ , a n a_{q_i},a_{q_{i+1}}, ···,a_n aqi,aqi+1,,an 升序排列。请求出操作完成后的序列。

    【输入格式】

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

    接下来 m m m 行描述对序列的操作,其中第 i i i 行包含两个整数 p i , q i p_i, q_{i} pi,qi 表示操作类型和参数。当 p i p_i pi = 0 时,表示将 a 1 , a 2 , ⋅ ⋅ ⋅ , a q i a_1, a_2, · · · , a_{q_i} a1,a2,,aqi 降序排列;当 p i = 1 p_i = 1 pi=1 时,表示将 a q i , a q i + 1 , ⋅ ⋅ ⋅ , a n a_{q_i} , a_{q_{i+1}}, · · · , a_n aqi,aqi+1,,an 升序排列。

    【输出格式】
    输出一行,包含 n n n 个整数,相邻的整数之间使用一个空格分隔,表示操作完成后的序列。

    【样例输入】

    3 3

    0 3

    1 2

    0 2

    【样例输出】

    3 1 2

    【样例说明】

    原数列为 (1, 2, 3)。

    第 1 步后为 (3, 2, 1)。

    第 2 步后为 (3, 1, 2)。

    第 3 步后为 (3, 1, 2)。与第 2 步操作后相同,因为前两个数已经是降序了。

    【评测用例规模与约定】

    对于 30% 的评测用例, n , m ≤ 1000 n, m ≤ 1000 n,m1000

    对于 60% 的评测用例, n , m ≤ 5000 n, m ≤ 5000 n,m5000

    对于所有评测用例, 1 ≤ n , m ≤ 100000 , 0 ≤ p i ≤ 1 , 1 ≤ q i ≤ n 1 ≤ n, m ≤ 100000,0 ≤ p_i ≤ 1,1 ≤ q_i ≤ n 1n,m1000000pi11qin

  • 解题思路

    有点线段树的味道,但不知道怎么处理,所以只能暴力出奇迹了,能过 60 % 60\% 60%的数据。

  • 偷分代码

/**
  *@filename:I
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-18 15:58
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;

int n,m;
int main(){
    while(cin>>n>>m){
        vector<int> a(n);
        for(int i=0;i<n;i++)a[i]=i+1;
        int p,q;
        while(m--){
            cin>>p>>q;
            if(p)sort(a.begin()+q-1,a.end());
            else sort(a.begin(),a.begin()+q,greater<int>() );
        }
        for(int i=0;i<n;i++){
            cout<<a[i];
            i==n-1?cout<<endl:cout<<" ";
        }
    }
    return 0;
}
  • 标程
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef pair<int,int> PII;
PII s[100005];
int n,m;
int top;
int ans[100005];
int main() {
	cin>>n>>m;
	int x,y;
	for(int i=1; i<=m; ++i) {
		scanf("%d%d",&x,&y);
		if(!x)//0操作 
		{
			if(top&&s[top].first==0) {//对于连续的0操作只保留一个 
				y=max(y,s[top--].second);
			}
			while(top>=2&&s[top-1].second<=y) {//0操作覆盖掉乱序区 
				top-=2;
			}
			s[++top]= make_pair(0,y);//插入 
		}else if(top)//栈不为空,且为1操作。
		{
			//因为是0/1是交替出现的,且只会有一个,所以这里取最小值即可。 
			if(top&&s[top].first==1) {
				y=min(y,s[top--].second);
			}
			while(top>=2&&s[top-1].second>=y) {
				top-=2;
			}
			s[++top]= make_pair(1,y);
		}
	}
	int k=n,l=1,r=n;
	for(int i=1; i<=top; ++i) {//将固定的数字插入ans数组里 
		if(s[i].first==0) {
			while(r>s[i].second&&l<=r) ans[r--]=k--;
		} else {
			while(l<s[i].second&&l<=r) ans[l++]=k--;
		}
		if(l>r) break;
	}
	
	//如果有数字没被固定,就还需要将它们存入ans数组里 
	if(top%2!=0) {
		while(k>0) ans[l++]=k--;
	} else {
		while(k>0) ans[r--]=k--;
	}
	for(int i=1;i<=n;++i)
	{
		printf("%d ",ans[i]);
	}
	return 0;
}


第十题 括号序列

  • 问题描述

    给定一个括号序列,要求尽可能少地添加若干括号使得括号序列变得合法,当添加完成后,会产生不同的添加结果,请问有多少种本质不同的添加结果。两个结果是本质不同的是指存在某个位置一个结果是左括号,而另一个是右括号。例如,对于括号序列 (((),只需要添加两个括号就能让其合法,有以下几种不同的添加结果:()()()、()(())、(())()、(()()) 和 ((()))。

    【输入格式】

    输入一行包含一个字符串 s s s,表示给定的括号序列,序列中只有左括号和右括号。

    【输出格式】

    输出一个整数表示答案,答案可能很大,请输出答案除以 1000000007 1000000007 1000000007(即 1 0 9 + 7 10^9 + 7 109+7) 的余数。

    【样例输入】

    ((()

    【样例输出】

    5

    【评测用例规模与约定】

    对于 40% 的评测用例, ∣ s ∣ ≤ 200 |s| ≤ 200 s200

    对于所有评测用例, 1 ≤ ∣ s ∣ ≤ 5000 1 ≤ |s| ≤ 5000 1s5000

  • 解题思路
    指路一篇blog:括号序列

  • 代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define vi vector<int>
#define vll vector<ll>
#define fi first
#define se second
const int maxn = 5000 + 5;
const int mod = 1e9 + 7;
int n;
int dp[maxn][maxn];
string a ;
ll calc ()
{
    memset(dp , 0 , sizeof dp);
    dp[0][0] = 1;
    for (int i = 1 ; i <= n ; i++){
        if (a[i - 1] == '('){
            for (int j = 1 ; j <= n ; j++)
                dp[i][j] = dp[i - 1][j - 1];
        }else {
            dp[i][0] = (dp[i - 1][0] + dp[i - 1][1]) % mod;
            for (int j = 1 ; j <= n ; j++)
                dp[i][j] = (dp[i - 1][j + 1] + dp[i][j - 1]) % mod;
        }
    }
    for (int i = 0 ; i <= n ; i++) if (dp[n][i]) return dp[n][i];
    return -1;
}
int main()
{
    ios::sync_with_stdio(false);
    cin >> a;
    n = a.size();
    ll l = calc();
    reverse(a.begin() , a.end());
    for (auto &g : a) if (g == '(') g = ')'; else g = '(';
    ll r = calc();
    cout << l * r % mod << endl;
    return 0;
}

总结

难度适中,梯度明显,整体还算OK,待结果出来还愿。希望能取得一个不错的结果。

来还愿了:一等奖,排名还靠前。
再次还愿:总决赛二等奖。

  • 38
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

unique-pure

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

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

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

打赏作者

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

抵扣说明:

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

余额充值