2021年度训练联盟热身训练赛第二场 全场题解

2021年度训练联盟热身训练赛第二场

A-Binarize It

题意

对于给定数,找到第一个大于等于它的2的次方。

思路

算出其二进制表达下有几位(假设 c n t cnt cnt 位),以及本身是否为2的次方,若是2的次方就输出本身,否则输出 2 c n t + 1 2^{cnt + 1} 2cnt+1

代码

#include <iostream>
using namespace std;
int main(){
    int T;
    cin >> T;
    while(T--){
        int n;
        cin >> n;
        cout << "Input value: " << n << endl;
        int cnt = 0;
        int flag = 0;
        while(n){
            if(n & 1){
                flag++;
            }
            n >>= 1;
            cnt++;
        }
        if(flag == 1){
            cnt--;
        }
        cout << (1 << cnt) << endl << endl;
    }
    return 0;
}

B-g2g c u l8r

题意

给定一系列语句的简称,并给出一个由一部分简称和正常语句构成的句子,把它替换成全称句子输出。

思路

字符串处理,简单模拟。具体见代码。

代码

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

map<string, string> mp;

string s, t, tmp;

inline void read(){	//简称对应全称的读入
    char c;
    t = "";
    getchar();
    while((c = getchar()) != '\n'){
        t.append(1, c);
    }
}

inline char space_read(){	//整个句子的读入,用返回值区分读到了空格还是回车
    char c;
    tmp = "";
    while((c = getchar()) != ' '){
        if(c == '\n'){
            return c;
        }
        tmp.append(1, c);
    }
    return c;
}

int main(){
    int T;
    cin >> T;
    while(T--){
        cin >> s;
        read(); //cin >> t
        mp[s] = t;
    }
    int q;
    cin >> q;
    getchar();
    for(int i = 0; i < q; i++){
        char c;
        while(c = space_read()){
            if(mp.find(tmp) == mp.end()){
                cout << tmp;
            }
            else{
                cout << mp[tmp];
            }
            if(c == '\n'){
                break;
            }
            cout << ' ';
        }
        cout << endl;
    }
    return 0;
}

C-Tip to be Palindrome

题意

大哥吃完饭给小费,给饭钱的20%向上取整,并且要求小费+饭钱的值是一个回文,如果不是回文就加小费到回文状态(加小费当然加的越少越好)。给定饭钱,输出小费和总费用。

思路

算出小费之后在当前小费+总费用的基础上一直加1直到为回文。cf上做过一道有点类似的题,总之找到回文的次数一定是在一个可控范围内的,所以暴力不会超时。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define PI acos(-1)
#define pb push_back
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 500 + 19;
const ll mod = 1e9 + 7;
const double eps = 1e-12;

string s;

bool isP(int x)
{
    s = "";
    while(x)
    {
        s.append(1, x % 10);
        x /= 10;
    }
    int n = s.length();
    for(int i = 0; i <= n / 2; i++)
    {
        if(s[i] != s[n - i - 1])
            return 0;
    }
    return 1;
}

int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        int n;
        cin >> n;
        cout << "Input cost: " << n << endl;
        int tip = n / 5;
        if(n - tip * 5 > 0)
            tip++;
        int bill = tip + n;
        while(!isP(bill))
        {
            bill++;
        }
        tip = bill - n;
        cout << tip << ' ' << bill << endl << endl;
    }
    return 0;
}

D-Soccer Standings

题意

n n n 支队伍, m m m 场比赛,输了队伍不扣分,赢了加三分,平局各加一分。要求你按照总分数对队伍进行降序排序,若分数相同按照进球数-被进球数降序排序,若这个也相等按照进球数降序排序,若进球数也相等按照名字字典序升序排序,并按序输出每个球队的信息,信息包括:名字,分数,赢场次数,输场次数,平局场次数,进球数,被进球数。

思路

简单模拟。硬模就行。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define PI acos(-1)
#define pb push_back
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 500 + 19;
const ll mod = 1e9 + 7;
const double eps = 1e-12;

map<string, int> mp;
struct node
{
    string name;
    int score;
    int goal;
    int allow;
    int win;
    int lose;
    int draw;
    bool operator < (const node& obj)const
    {
        if(score == obj.score)
        {
            if(goal - allow == obj.goal - obj.allow)
            {
                if(goal == obj.goal)
                {
                    return name > obj.name;
                }
                return goal < obj.goal;
            }
            return goal - allow < obj.goal - obj.allow;
        }
        return score < obj.score;
    }
    void clear()
    {
        score = 0;
        goal = 0;
        allow = 0;
        win = 0;
        draw = 0;
        lose = 0;
    }
} a[N];

int main()
{
    int T;
    cin >> T;
    for(int C = 1; C <= T; C++)
    {
        int n, m;
        cin >> n >> m;
        mp.clear();
        for(int i = 0; i < n; i++)
        {
            a[i].clear();
        }
        for(int i = 0; i < n; i++)
        {
            cin >> a[i].name;
            mp[a[i].name] = i;
        }
        string x, y;
        for(int i = 0, xs, ys; i < m; i++)
        {
            cin >> x >> xs >> y >> ys;
            if(xs > ys)
            {
                a[mp[x]].score += 3;
                a[mp[x]].win++;
                a[mp[y]].lose++;
            }
            else if(xs < ys)
            {
                a[mp[y]].score += 3;
                a[mp[x]].lose++;
                a[mp[y]].win++;
            }
            else
            {
                a[mp[x]].score++;
                a[mp[y]].score++;
                a[mp[x]].draw++;
                a[mp[y]].draw++;
            }
            a[mp[x]].goal += xs;
            a[mp[x]].allow += ys;
            a[mp[y]].goal += ys;
            a[mp[y]].allow += xs;
        }
        sort(a, a + n);
        cout << "Group " << C << ":" << endl;
        for(int i = n - 1; i >= 0; i--)
        {
            cout << a[i].name << ' ' <<
            a[i].score << ' ' <<
            a[i].win << ' ' <<
            a[i].lose << ' ' <<
            a[i].draw << ' ' <<
            a[i].goal << ' ' <<
            a[i].allow << endl;
        }
        cout << endl;
    }
    return 0;
}

E-NIH Budget

题意

d d d 种疾病,每种疾病所投入的钱和所能够救的人不成线性关系,而是一个4段的离散的函数(类似收停车费),有4个等级的投入值,投入钱超过某个等级的值就能救对应量的人。超过这个值但是未达到下一个等级,也只能救前一个等级的人数。

Research FundingLives Saved
10 million5
50 million100
100 million1000
250 million1100

现在给定每个疾病四个分级需要投入的钱和能救的人数,要求你算出在总投入小于给定值的情况下,最多能救多少人?

思路

第一次赛场上这么直接的看出来一道dp题(不是),但最后不是我写的,赛后补也确实发现没有想象的那么容易,不过也不是很难,还是可以做的。

转化问题为:给定背包容量和每个物品的占用容量、价值,最大化背包中物品的价值。没错,是01背包。但是有一个点需要改变,就是一个疾病的4个等级不能看作单独的物品,这四个物品是只能四选一的,所以你相当于要再这四个里面取max而不是按照一般做法把他们都当成单独物品。这点只要稍微改变一下循环就可以,滚动或者二维都行。具体见代码

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define PI acos(-1)
#define pb push_back
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 100 + 19;
const int M = 100000;
const ll mod = 1e9 + 7;
const double eps = 1e-12;

int cost[N][5];
int save[N][5];
int dp[M];

int main()
{
    int T;
    scanf("%d", &T);
    for(int C = 1; C <= T; C++)
    {
        int d, b;
        scanf("%d%d", &d, &b);
        fill(dp, dp + b + 1, 0);
        for(int i = 1; i <= d; i++)
        {
            for(int j = 1; j <= 4; j++)
            {
                scanf("%d%d", &cost[i][j], &save[i][j]);
            }
        }
        //dp[i] = max_save
        for(int i = 1; i <= d; i++)
        {
            for(int l = b; l >= 0; l--)
            {
                for(int j = 1; j <= 4; j++)
                {
                    if(l >= cost[i][j])
                        dp[l] = max(dp[l], dp[l - cost[i][j]] + save[i][j]);
                }
            }
        }
        int ans = 0;
        for(int i = 0; i <= b; i++)
        {
            ans = max(ans, dp[i]);
        }
        printf("Budget #%d: Maximum of %d lives saved.\n\n", C, ans);
    }
    return 0;
}

F-Interstellar Love

题意

给定 n n n 个点 m m m 条路,求出其中有几个顶点数大于1的连通分量,以及其中有几个是环。

思路

并查集求连通分量,dfs求环。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define PI acos(-1)
#define pb push_back
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1000 + 19;
const int M = 100000;
const ll mod = 1e9 + 7;
const double eps = 1e-12;

int par[N];
int rnk[N];
int num[N];
bool vis[N];
int n, m;
vector<int> vec[N];
set<int> st;

void init(int n)
{
    st.clear();
    for(int i = 0; i <= n; i++)
    {
        par[i] = i;
        rnk[i] = 0;
        num[i] = 0;
        vis[i] = false;
        vec[i].clear();
    }
}

int find(int x)
{
    if(x == par[x])
        return x;
    return par[x] = find(par[x]);
}

void unite(int x, int y)
{
    x = find(x);
    y = find(y);
    if(x == y)
        return;
    if(rnk[x] > rnk[y])
    {
        par[y] = x;
    }
    else
    {
        par[x] = y;
        if(x == y)
            rnk[x]++;
    }
}

void addEdge(int u, int v)
{
    vec[u].pb(v);
    vec[v].pb(u);
}

bool dfs(int u, int pre)
{
    if(vis[u])
    {
        return 0;
    }
    vis[u] = 1;
    for(auto v : vec[u])
    {
        if(v == pre)
            continue;
        if(dfs(v, u))
            continue;
        return 0;
    }
    return 1;
}

int main()
{
    int T;
    scanf("%d", &T);
    for(int C = 1; C <= T; C++)
    {
        cin >> n >> m;
        init(n);
        for(int i = 0, u, v; i < m; i++)
        {
            cin >> u >> v;
            addEdge(u, v);
            unite(u, v);
        }
        for(int i = 1; i <= n; i++)
        {
            st.insert(find(i));
            num[find(i)]++;
        }
        int cnt = 0;
        int single = 0;
        for(auto i : st)
        {
            if(num[find(i)] <= 1)
                single++;
            if(!dfs(i, 0))
            {
                cnt++;
            }
        }
        printf("Night sky #%d: %d constellations, of which %d need to be fixed.\n\n",
                C, st.size() - single, cnt);
    }
    return 0;
}

G-Plate Spinning

题意

n n n 个盘子连成环,最开始都以 p p p 的速度旋转,每秒钟转速减少 5 5 5 ,转速为 0 0 0 时盘子掉落。杂技师可以将一个转速大于等于 0 0 0 的盘子重新转到 p p p 的速度,他从一个盘子换到另一个盘子需要 0.5 0.5 0.5 秒的时间,且给盘子加速不需要花时间。问对于给定的 n , p n, p n,p ,杂技师能保持所有盘子不掉吗?

思路

比赛卡了老久,其实最开始题意理解正确,只是没特判1,以为理解错题意了,于是逐渐往离谱但是符合题目描述的方向走去,,,最后大胆假(窃)设(听) n = 1 n = 1 n=1 的情况是算永远成功,且是加速到 p p p ,才终于过了。

其实很简单,无论你怎么去转一定有一个盘子是最后才转到的,去判断你能否转到最后一个盘子就行。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define PI acos(-1)
#define pb push_back
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 500 + 19;
const ll mod = 1e9 + 7;
const double eps = 1e-12;

int main()
{
    int T;
    cin >> T;
    for(int i = 1; i <= T; i++)
    {
        int n, p;
        cin >> n >> p;
        printf("Circus Act %d:\n", i);
        if(n == 1){
            printf("Chester can do it!\n\n");
            continue;
        }
        if(5 * n <= p * 2)
            printf("Chester can do it!\n\n");
        else
            printf("Chester will fail!\n\n");
    }
    return 0;
}

H-The Eternal Quest for Caffeine

题意

一个人从自己所在的楼层出发去买快乐水,每层楼都有汽水机,但是都只有一部分零件在运作,他要拿到所有的零件,去某一个有他需要的气泡水的汽水机,修好汽水机、买好饮料并回到原楼层。问最短需要走几层楼(从本楼层出发并回到原楼层)?

思路

这题真就阅读理解,原谅我用翻译软件了(小声

数据范围极小,所以直接枚举走到的最下面的楼层、走到的最上面的楼层,若当前情况满足题意,更新答案(取min)。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define pb push_back
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 10 + 19;
const int M = 100000;
const ll mod = 1e9 + 7;
const double eps = 1e-12;

vector<int> vec[N];
set<int> st;
char vis[N];
int n, f, p;

void init(int n)
{
    for(int i = 0; i <= n; i++)
        vec[i].clear();
}

void unite(int i)
{
    for(auto j : vec[i])
        st.insert(j);
}

bool ok(int down, int up)
{
    st.clear();
    bool flag = 0;
    for(int i = down; i <= up; i++)
    {
        if(vis[i] == 'Y')
            flag = 1;
        unite(i);
    }
    if(st.size() == p && flag)
        return 1;
    return 0;
}

int main()
{
    int cc = 0;
    while(cin >> n >> f >> p)
    {
        if(!n && !f && !p)
            break;
        
        init(n);
        for(int i = 1, k; i <= n; i++)
        {
            cin >> k;
            for(int j = 0, x; j < k; j++)
            {
                cin >> x;
                vec[i].pb(x);
            }
            cin >> vis[i];
        }
        
        int ans = INF;
        for(int i = 0; i <= f; i++)
            for(int j = f; j <= n; j++)
                if(ok(i, j))
                    ans = min(ans, (f - i) * 2 + (j - f) * 2);
        
        if(ans == INF)
            printf("Test case #%d: Impossible\n\n", ++cc);
        else
            printf("Test case #%d: %d\n\n", ++cc, ans);
    }
    return 0;
}

I-Pegasus Circle Shortcut

题意

给定圆的圆心、起点、终点(保证起点终点在圆上),并给定一条从起点到终点共有 n n n 的点的路,问是从圆上逆时针走比较进还是从路上走比较进。

思路

比赛因为精度WA了很多次,加起来这次比赛我WA了9次(#°Д°)

简单模拟,就算出来然后比较就行了,但是最开始我在算弧长的时候套了一个精度不够的公式,是用起点和终点算弦长然后反过来求圆心角从而求出弧长,最后判断叉积是否小于0(小于0逆时针角大于180度)来决定是否要取较大的半圆。疯狂WA找不出错误之后就开始怀疑公式有问题,可能是弦长求圆心角的精度不够。

于是开始找有没有能够算出两个向量逆时针夹角的公式,最后找到 atan2() 函数,表示从 x x x 轴正方向逆时针转到该向量的角度,如果是负的就说明夹角超过180度(顺时针转的角度为其绝对值)。因此可以算出终点、起点以圆心为原点和 x x x 轴正方向的逆时针转角之差,并规范化(就是使之处于 [ 0 , 2 π ) [0, 2\pi) [0,2π) 之间),也就不需要判断叉积了。求出角度后,弧长就是圆心角乘半径。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define PI acos(-1)
#define pb push_back
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 500 + 19;
const ll mod = 1e9 + 7;
const double eps = 1e-12;

struct Point
{
    double x, y;
    Point() {}
    Point(double a, double b)
    {
        x = a;
        y = b;
    }
    bool ed()//判断输入结尾
    {
        return fabs(x) < eps && fabs(y) < eps;
    }
};

double dis(Point a, Point b)//距离
{
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

int cross(Point& point1, Point& point2)//向量叉积(本题最后没有用到
{
    double temp = point1.x * point2.y - point2.x * point1.y;
    return temp;
}

Point a[N];

int main()
{
    Point c, s, t;
    for(int T = 1; ; T++)
    {
        scanf("%lf%lf%lf%lf%lf%lf", &c.x, &c.y, &s.x, &s.y, &t.x, &t.y);
        if(c.ed() && s.ed() && t.ed())
            break;
        
        int n;
        cin >> n;
        for(int i = 0; i < n; i++)
            cin >> a[i].x >> a[i].y;
        
        //按圆逆时针走
        double ans1 = atan2(t.y - c.y, t.x - c.x) - atan2(s.y - c.y, s.x - c.x);
        while (ans1 < 0.0)
            ans1 += 2 * PI;
        while (ans1  > 2 * PI)
            ans1 -= 2 * PI;
        ans1 *= dis(s, c);
        
/*		精度不够的做法
        double r = dis(s, c);
        double ans1 = 2.0 * r * asin(dis(s, t) / (2.0 * r));
        if(cross(s, t) < 0.0)
            ans1 = 2.0 * PI * r - ans1;
*/
        //按路走
        double ans2 = dis(s, a[0]);
        for(int i = 1; i < n; i++)
            ans2 += dis(a[i - 1], a[i]);
        ans2 += dis(a[n - 1], t);
        
        printf("Case #%d: ", T);
        if(ans1 > ans2)
        {
            printf("Watch out for squirrels!\n\n");
        }
        else
        {
            printf("Stick to the Circle.\n\n");
        }
    }
    return 0;
}

J-Lowest Common Ancestor

题意

给定两个16进制表示下的数,算出他们在二进制表示下的最长公共前缀。

思路

题的本意是找两个数在完美二叉树层序编号下的最近公共祖先,但其实层序编号下的数的二进制表示就是树按照左0右1的情况走下来的二进制表示,也就是离散里的哈夫曼编码。因此就是找二进制下的公共前缀。

公共前缀让我梦回计网课算子网掩码和IP网段,我期末没90一定是错这题了(误)

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define pb push_back
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1000 + 19;
const int M = 100000;
const ll mod = 1e9 + 7;
const double eps = 1e-12;

void change(int x, string& a)
{
    while(x)
    {
        if(x & 1)
        {
            a.append(1, '1');
        }
        else
        {
            a.append(1, '0');
        }
        x >>= 1;
    }
    reverse(a.begin(), a.end());
}

int trans(char a)
{
    if (a <= '9')
        return a - '0';
    return a - 'a' + 10;
}

inline string to_binary(char c)
{
    return bitset<4>(trans(c)).to_string();
}

inline string to_binary_string(string& a)
{
    string res;
    for (int i = 0; i < a.length(); ++i)
    {
        res += to_binary(a[i]);
    }
    int i = 0;
    while (i < res.length() && res[i] == '0')
    {
        ++i;
    }
    return res.substr(i);
}

char to_hex(int n) {
    if (n <= 9) return n + '0';
    else return n + 'a' - 10;
}

string to_hex_string(string& a) {
    int tmp;
    string res;
    for (int i = a.length() - 1; i >= 0; i -= 4) {
        tmp = 0;
        for (int j = -3; j <= 0; ++j) {
            if (i + j < 0) {
                continue;
            }
            tmp = tmp * 2 + (a[i + j] - '0');
        }

        res = to_hex(tmp) + res;

    }
    return res;
}

int main()
{
    int n;
    cin >> n;
    for(int j = 1; j <= n; j++)
    {
        string a, b;
        cin >> a >> b;
        a = to_binary_string(a);
        b = to_binary_string(b);
        int k = min(a.length(), b.length());
        int cnt = 0;
        for(int i = 0; i < k; i++)
        {
            cnt = i;
            if(a[i] != b[i])
            {
                cnt--;
                break;
            }
        }
        a = a.substr(0, cnt + 1);
        a = to_hex_string(a);
        cout << "Case #" << j << ": " << a << "\n\n";
    }
    return 0;
}

总结

第一次AK,虽然靠队友的比较多qwq

第一道开的题就是G题,因为题意不清晰以及最开始做的时候不仔细没特判所以WA了很多次,但是说实话要是最开始细心一点说不定之后都不需要纠结题意。

之后开的是I题,没错也卡了,卡到最后一题才出这个。而且是因为精度而不是做的不对,怪就怪我没有计算几何板子,,,下周就校赛了,计算几何的板子是肯定要理了,其他很需要板子的估计也得理,要加油了

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值