2019年湘潭大学程序设计竞赛

鉴于博主是个铁憨憨,只A了5题,大佬还请轻喷

Who’s better?

ICPC比赛中,谁通过的题数多,谁排名靠前;在通过题数相同的情况下,谁的罚时少,谁排名靠前;如果前两者都相同,就看最后正确提交的时间,谁早最排名靠前。 现在给你两个队伍的正确通过的题数、罚时和最后正确提交时间,请判断一下,谁的排名更靠前?
输入描述:
只有一组测试样例,两行,每行三个整数
n(0≤n≤13),p(1≤p≤1000),s(1≤s≤300),依次表示两个队的正确通过的题数、罚时和最后正确提交时间。

输出描述:
输出一行(末尾要换行符)。
如果是第1个队排名靠前,输出1;如果是2队,输出2;如果无法分辨,输出"God"。

示例1
输入

1 10 10
1 22 2
输出

1

示例2
输入

1 10 10
2 42 20
输出

2
示例3
输入

1 10 10
1 10 10
输出

God

没什么好说的,按照题目要求直接做

#include <iostream>
#include <algorithm>
using namespace std;

typedef struct Node{
    int n, p, s;
    Node(int n, int p, int s){
        this->n = n;
        this->p = p;
        this->s = s;
    }
    bool operator == (const Node & a){
        if(this->n == a.n && this->p == a.p && this->s == a.s)
            return true;
        return false;
    }
    bool operator > (const Node & a){
        if(this->n > a.n)
            return true;
        else if(this->n < a.n)
            return false;
        if(this->p < a.p)
            return true;
        else if(this->p > a.p)
            return false;
        if(this->s < a.s)
            return true;
        return false;
    }
}Node;

int main(){
    int a, b, c;
    int x, y, z;
    cin >> a >> b >> c >> x >> y >> z;
    Node node1(a, b, c), node2(x, y, z);
    if(node1 == node2){
        cout << "God" << endl;
        return 0;
    }
    if(node1 > node2){
        cout << 1 << endl;
    }else{
        cout << 2 << endl;
    }
    return 0;
}

Number

Bonnie得到了一个数字n。
现在她想对这个数字不断的做一种操作:
如果n的最后一位数码是0,那么她就把n除以10;
否则她把这个数加上1;
直到n变为一个不大于1的数。
给定n,请问Bonnie需要做多少次操作?
输入描述:
第一行一个数字
T(1≤T≤300000)–样例个数。
每个样例仅一行一个数字
n(1≤n≤10^9) 。
输出描述:
每个样例输出一行一个数字—Bonnie需要做的操作次数。

输入

6
9
99
2
11032
1000000000
62
输出

2
3
9
44
9
13
说明

第一个样例:
9→10→1

第二个样例:
99→100→10→1

没什么好说的,按照题目要求直接做。因为n最多只有九位数,所以一个数最多处理不超过100次,T总共300000个,所以时间完全够

#include <iostream>
#include <algorithm>
using namespace std;

int change_num(int x){
    int n = 0;
    while(x != 1){
        ++n;
        
        if(!(x%10))
            x /= 10;
        else
            x++;
    }
    return n;
}

int main(){
    int n;
    cin >> n;
    while(n--){
        int x;
        cin >> x;
        cout << change_num(x) << endl;
    }
    return 0;
}

Math Problem

题目描述
已知整数
a,a^3除192的余数是1。求区间[L,R]之间满足条件的a的累加和是多少?
输入描述:
第一行是一个整数
T(1≤T≤10000),表示样例的个数。
每个样例包含两个整数
L,R,1≤L≤R≤10^9。
输出描述:
每行输出一个样例的结果。
示例1
输入

1
1 10
输出

1

基本数学运算。
题目要求是:a^3 % 192 == 1,a^3-1可以转换成其他式子a^3-1 = (a-1)*(a*(a+1)+1)
根据题目要求可以发现 a^3-1 可以整出 192,又因为(a*(a+1)+1)必定是奇数(可以自己计算),所以192 = 64*3中的64必定来自(a-1)。
设(a*(a+1)+1)%3 == 0,这个时候可以证明当且仅当a = 3k+1(k为常数)时 等式成立。所以很容易证明 (a-1)%3==0。
综上:(a-1)能够整除64,也能整除3,所以(a-1)可以整除192。所以a%192=1。
所以a的取值就是 a = 192*k + 1 (k = 1,2,3,4,5.....)
计算区间L~R的a的和,就是区间 (0~R)的和 减去 区间 (0~L-1) 的和。
又 原式 为等差数列,通过公式直接求即可
#include <iostream>
#include <algorithm>
using namespace std;

long long int get_n(int x){
    if(x == 0)
        return 0;
    long long int n = (x-1)/192;
    long long int sum = 0;

    sum = (1 + n) * n / 2 * 192;

    return sum + 1 + n;
}

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

Stone

题目描述
有n堆石子排成一排,第i堆石子有ai个石子。
每次,你可以选择任意相邻的两堆石子进行合并,合并后的石子数量为两堆石子的和,消耗的体力等价于两堆石子中石子数少的那个。
请问,将所有的石子合并成一堆,你所消耗的体力最小是多少?
输入描述:
第一行是一个整数
T(1≤T≤20),表示样例的个数。
每个样例的第一行是一个整数n(1≤n≤10000),表示石子堆的数量。
第二行是n个整数
ai(1≤ai≤10^9)

输出描述:
每行输出一个样例的结果。
示例1
输入

2
2
1 2
1
1
输出

1
0

简单贪心。
一开始以为是每次取最小的两个石头求和,再放回原数组。但是这样子和明显不对,看下面例子就知道了。
5
1 1 1 1 1
如果按照上述贪心方法,答案应该是 1+1+1+2=5
但是有更少的方法:固定一个最大的不边,每次都让最小的和最大的求和。
即:1+1+1+1=4
例如:5
1 2 3 4 5 
就是:
第一轮 1+5 = 6 sum += 1。数组变成  2 3 4 6
第二轮 2+6 = 8 sum += 2。数组变成 3 4 8
第三轮 3+8 = 11 sum ++ 3。数组编程 4 11
第四轮 4 + 11 = 15 sum += 4。数组编程 15
sum = 10
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
using namespace std;
vector<int> v;
int main(){
    int n;
    int T;
    cin >> T;
    while(T--){
        cin >> n;
        for(int i=0; i<n; i++){
            int t;
            cin >> t;
            v.push_back(t);
        }
        sort(v.begin(), v.end());
        long long int sum = 0;
        for(int i=0; i<v.size()-1; i++){
            sum += v[i];
        }
        cout << sum << endl;
        v.clear();
    }
    return 0;
}

Black & White

题目描述
你有一个长度为 n 的 01 串S,你可以执行最多 m 次操作。
对于每次操作,你可以选择一个位置 i 满足
1≤i≤n,翻转这一位的值,0变成1,1变成0。
定义一个 01 串的价值为其中最长连续0的个数和最长连续1的个数的较大值,求S在经过最多m次操作后的最大价值。
输入描述:

  • 第一行一个整数 T ,表示接下来有 T 个样例。
  • 首先输入n,m,表示S串的长度n和操作次数m,其中
    1≤n≤100000,0≤m≤10000;
  • 接下来输入一个长度为n的字符串S。
    输出描述:
    一个整数,表示题面上描述的最大价值。
    示例1
    输入

2
5 1
00101
2 1
01
输出

4
2
说明

第一个串翻转第三个位置,00001的价值为4;第二个串翻转第一个位置,11的价值为2。

这题的想法时前缀和+二分查找。
首先预处理两个数组 pre1 和 pre0,分表代表1~i个位置上1和0的个数
这样就可以在 O(1) 的时间求出 区间内 1或者0 的个数
然后二分答案:最大长度len。
因为 len 是固定的,所以 l=1,r=len 开始 滑动窗口(时间O(n)),每次移动都判断当前方块内 1或者0的个数是否小于 可以改变的次数m,如果小于等于 则 len 可以成立,可以往更大值二分,否则只能往更小值二分。
时间复杂度:每次二分判断需要O(n)的时间,二分需要log2(n)的时间,所以总时间n*lg(n)时间复杂度ok
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
string str;
int n, m;
int pre0[100010], pre1[100010];
bool judjed(int len){

    int l = 1, r = len;
    while(r <= n){
        //out << "len = "<< len << " l = " << l << " r = " << r << " 0 = " << pre0[r]-pre0[l-1] << " 1 = " << pre1[r]-pre1[l-1] << endl;
        if(pre0[r]-pre0[l-1] <= m || pre1[r]-pre1[l-1] <= m)
            return true;
        ++l, ++r;
    }
    return false;
}

int get_n(){
    int res;

    int l=0, r=n;
    while(l <= r){
        int mid = (l+r)/2;
        if(judjed(mid)){
            l = mid+1;
            res = mid;
        }else{
            r = mid-1;
        }
    }
    return res;   
}

int main(){
    int T;
    cin >> T;
    while(T--){
        cin >> n >> m;
        cin >> str;
        for(int i=1; i<=n; i++){
            if(str[i-1] == '0'){
                pre0[i] = pre0[i-1]+1;
                pre1[i] = pre1[i-1];
            }else{
                pre0[i] = pre0[i-1];
                pre1[i] = pre1[i-1]+1;
            }
        }
        cout << get_n() << endl;
        str.clear();
    }
    return 0;
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值