「团队训练赛」The 2021 Sichuan Provincial Collegiate Programming Contest 题解

The 2021 Sichuan Provincial Collegiate Programming Contest

A - Chuanpai

题目描述:

你有一个骰子,扔两次,得到的数字和等于k的方案是多少,其中(3, 4) 和 (4,3)属于一种方案

思路:

显然小于2和大于12的方案都是0

其他的模拟一下就行

//Work by: Chelsea
#include 
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod 1000000007
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 300000 + 50
int n, m, k, op;
int x, y, z;
ll a, b, c;
string s, t;
int tr[MAX];

void work(){
    cin >> n;
    if(n > 12 || n < 2)cout << 0 << endl;
    else {
        int num = 0;
        for(int i = 1; i <= 6; ++i){
            if(n - i < i)break;
            if(n - i >= 1 && n - i <= 6)++num;
        }
        cout << num << endl;
    }
}


int main(){
    io;
    int tt;cin>>tt;
    for(int _t = 1; _t <= tt; ++_t){
        work();
    }
    return 0;
}

B - Hotpot

题目描述:

n个人吃旋转小火锅,转盘上最开始什么都没有,每个人都只吃一种东西,记作a[i],火锅会旋转k轮,第i轮轮到编号为k%n的人吃东西,如果当前锅里没有他喜欢吃的东西,他就会让人在锅中放他喜欢吃的东西,此时他不能吃;如果当前锅中有他喜欢吃的东西,那就会直接吃掉。问k轮后每个人吃的东西的数量

思路:

首先,如果对于某个人,

  • 如果他喜欢吃的食物在这n个人中是独一无二的,那我们只需要计算他能吃几轮lun[i],答案就是lun[i]/2,因为他只能在这一轮放,在下一轮吃

  • 如果他喜欢吃的食物在这n个人中不是独一无二的,且有相同爱好的人的数量是num,记pos是在具有相同爱好中第几个出现的

    • 如果num是偶数,
      • pos是奇数的人一定是吃不到的
      • pos是偶数的人是一定每轮都有的吃
    • 如果num是奇数,
      • pos是奇数的人吃到的数量是 ⌊ l u n [ i ] 2 ⌋ \lfloor\frac{lun[i]}{2}\rfloor 2lun[i]
      • pos是偶数的人吃到的数量是 ⌈ l u n [ i ] 2 ⌉ \lceil\frac{lun[i]}{2}\rceil 2lun[i]
//Work by: Chelsea
#include 
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod 1000000007
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 300000 + 50
int n, m, k, op;
int x, y, z;
ll a, b, c;
string s, t;
int tr[MAX];
int lun[MAX];
bool vis[MAX];
int ans[MAX];
void work(){
    cin >> n >> k >> m;
    map<int, int>mp;
    for(int i = 0; i < n; ++i){
        vis[i] = 0;
        cin >> tr[i];
        ++mp[tr[i]];
        if(mp[tr[i]] % 2)vis[i] = 1;
    }
    for(int i = 0; i < n; ++i){
        lun[i] = m / n + ((m % n > i) ? 1 : 0);
    }
    for(int i = 0; i < n; ++i){
        if(mp[tr[i]] == 1)ans[i] = lun[i] / 2;
        else{
            if(mp[tr[i]] % 2 == 0){
                if(vis[i])ans[i] = 0;
                else ans[i] = lun[i];
            }
            else{
                if(vis[i])ans[i] = lun[i] / 2;
                else ans[i] = (lun[i] + 1) / 2;
            }
        }
        cout << ans[i] << " \n"[i == n - 1];
    }
}


int main(){
    io;
    int tt;cin>>tt;
    for(int _t = 1; _t <= tt; ++_t){
        work();
    }
    return 0;
}

D - Rock Paper Scissors

题目描述:

石头剪刀布游戏,现在Alice和Bob有同数量的卡片,两个人互相知道对方的卡片的种类和数量

现在Bob先出卡片,Alice再出,Alice赢了则分数+1,Bob赢了则分数-1,Alice想让分数尽可能大,Bob想让分数尽可能小,问分数最后是多少

思路:

贪心,Bob的出牌顺序是无所谓的,Bob出一张牌,Alice先选能吃掉他的牌,如果没有了的话,就选与他种类相同的牌,如果还没有,则只能出被他吃的牌,计算答案就行

#include 
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod 1000000007
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
#define int long long
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 300000 + 50
int n, m, k, op;
int x, y, z;
ll a, b, c;
string s, t;
int tr[MAX];
int Bob[5], Alice[5];
int id[5];
void work(){
    id[1] = 2;id[2] = 3;id[3] = 1;
    for(int i = 1; i <= 3; ++i)cin >> Bob[i];
    for(int i = 1; i <= 3; ++i)cin >> Alice[i];
    int ans = 0;
    for(int i = 1; i <= 3; ++i){
        int idd = id[i];
        if(Alice[idd] >= Bob[i]){
            ans += Bob[i];
            Alice[idd] -= Bob[i];
        }
        else{
            ans += Alice[idd];
            Bob[i] -= Alice[idd];
            Alice[idd] = 0;
            if(Alice[i] >= Bob[i]){
                Alice[i] -= Bob[i];
                Bob[i] = 0;
            }
            else{
                ans -= (Bob[i] - Alice[i]);
                Alice[i] = 0;
                Bob[i] = 0;
            }
        }
    }
    cout << ans << endl;
}


signed main(){
    io;
    int tt;cin>>tt;
    for(int _t = 1; _t <= tt; ++_t){
        work();
    }
    return 0;
}


E - Don’t Really Like How The Story Ends

题目描述:

给你一个图,问最少加几条边能让图的dfs序是1 2 3…n

思路:

dfs + 贪心

建图的时候建一条从小节点到大节点的单向边就行

然后从1开始爆搜,遇到小于当前节点tot的就跳过,遇到大于当前节点的就说明需要连边了,更新答案和tot,然后去dfs(tot-1),如果当前这个点有能连到tot的边,那就说明不需要连边,直接更新tot后去dfs(tot-1)就行

//Work by: Chelsea
#include 
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod 1000000007
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 300000 + 50
int n, m, k, op;
int x, y, z;
ll a, b, c;
string s, t;
vector<int>G[MAX];
int tot, ans;
void dfs(int u){
    if(u > n)return;
    for(auto v : G[u]){
        if(v < tot)continue;
        while(tot <= v){
            if(v == tot){
                ++tot;
                dfs(tot - 1);
            }
            else {
                ++tot;
                ++ans;
                dfs(tot - 1);
            }
        }
    }
}

void work(){
    cin >> n >> m;
    tot = 2;ans = 0;
    for(int i = 1; i <= n; ++i)G[i].clear();
    for(int i = 1; i <= m; ++i){
        cin >> x >> y;
        if(x > y)swap(x, y);
        G[x].push_back(y);
    }
    G[1].push_back(n + 1);
    for(int i = 1; i <= n; ++i){
        sort(G[i].begin(), G[i].end());
        G[i].erase(unique(G[i].begin(), G[i].end()), G[i].end());
    }
    dfs(1);
    cout << ans << endl;
}


int main(){
    io;
    int tt;cin>>tt;
    for(int _t = 1; _t <= tt; ++_t){
        work();;
    }
    return 0;
}

H - Nihongo wa Muzukashii Desu

队友写的,好像是模拟题

虽然说因为从不写模拟题导致天梯赛打崩了,但我还是不想补模拟,咕咕咕

K - K-skip Permutation

题目描述:

构造一个全排列a[i],使的a[i] + k = a[i+1]i的数量最大

思路:

贪心就行

#include 
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod7 1000000007
#define mod9 998244353
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 1000000 + 50
int n, m, k, x;
int tr[MAX];

void work(){
    cin >> n >> k;
    set<int>se;
    for(int i = 2; i <= n; ++i)se.insert(i);
    tr[1] = 1;
    for(int i = 2; i <= n; ++i){
        int p = tr[i - 1] + k;
        if(p <= n && se.count(p)){
            tr[i] = p;
            se.erase(p);
        }
        else{
            tr[i] = *se.begin();
            se.erase(*se.begin());
        }
    }
    for(int i = 1; i <= n; ++i)cout << tr[i] << " \n"[i == n];
}


int main(){
    io;
    work();
    return 0;
}

L - Spicy Restaurant

题目描述:

n个点,每个点都有一个权值c,q次询问,每次询问都给你一个sp,问从s出发,到任意一个c<=p的点最短距离是多少

思路:

多源bfs

因为权值c很小,我们可以开一个dis[i][j]数组,表示从i出发到达权值为j的点的最短距离

我们跑100次bfs,每次都把权值为i的点塞到队列里面去求dis[i][j],跑完以后我们需要进一步的更新数组的含义为dis[i][j]表示从i出发到达权值小于等于j的点的最短距离

dis[i][j] = min(dis[i][j], dis[i][j - 1])

对于每次询问就直接判断是不是inf后输出

#include 
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod7 1000000007
#define mod9 998244353
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 100000 + 50
int n, m, k, x, y;
int tr[MAX];
vector<int>G[MAX];
int dis[MAX][105];

void bfs(int p){
    queue<int>q;
    for(int i = 1; i <= n; ++i){
        if(tr[i] == p){
            q.push(i);
            dis[i][p] = 0;
        }
    }
    while (!q.empty()) {
        int u = q.front();q.pop();
        for(auto v : G[u]){
            if(dis[v][p] == inf){
                q.push(v);
            }
            dis[v][p] = min(dis[v][p], dis[u][p] + 1);
        }
    }
}

void work(){
    scanf("%d%d%d", &n, &m, &k);
    mem(dis, inf);
    for(int i = 1; i <= n; ++i)scanf("%d", &tr[i]);
    for(int i = 1; i <= m; ++i){
        scanf("%d%d",&x, &y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    for(int i = 1; i <= 100; ++i)bfs(i);
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= 100; ++j){
            dis[i][j] = min(dis[i][j], dis[i][j - 1]);
        }
    }
    while (k--) {
        scanf("%d%d",&x, &y);
        if(dis[x][y] == inf)cout << -1 << endl;
        else cout << dis[x][y] << endl;
    }
}


int main(){
    work();
    return 0;
}

M - True Story

题目描述:

n个人处于同一个位置,距离机场的距离都是x,登机时间会改变k次,每次都会给出一个a,b表示从a开始获知登机时间改变,且改变到b,每个人有不同的速度,每个人在每次获知登机时间后都会进行一次判断,判断当前时间内能否到达机场,如果可以,就走,如果不可以,就一点也不走,问最终有几个点能到达机场

思路:

显然,不存在一个人走走停停的情况,也就是只要判断能走到,就一定会到

我们只需要找到所有时间段中最大的那一段,判断每个人在这一段能不能走到机场即可,注意别忘了最开始的那一段,还得开longlong

#include 
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod7 1000000007
#define mod9 998244353
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 300000 + 50
ll n, m, k, x;
ll tr[MAX];
ll t[MAX], p[MAX];
void work(){
    cin >> n >> m >> k >> x;
    for(int i = 1; i <= n; ++i)cin >> tr[i];
    for(int i = 1; i <= m; ++i)cin >> t[i];
    for(int i = 1; i <= m; ++i)cin >> p[i];
    ll maxn = x;
    for(int i = 1; i <= m; ++i)maxn = max(maxn, p[i] - t[i]);
    ll ans = 0;
    for(int i = 1; i <= n; ++i){
        if(maxn * tr[i] >= k)++ans;
    }
    cout << ans << endl;
}


int main(){
    io;
    work();
    return 0;
}

总结

这次比赛是我找的21年的四川省赛,本来的计划是3人1机+打印题面,想认真打一场省赛,看看自己水平到底在哪里,但是被lls突如其来的通知給破防了,只能三人三机

一般来说都是我和djk一起开题,yj自己开题,我这边两个人边读边写,我手速快,所以我们俩想完以后我就直接写了,yj那边也是写了两个题,拿了两个一血,导致开局一个小时十一分钟我们队就7个题了,一度冲到了第五名,但是后来被E卡了俩小时,这个题我们知道思路是什么,但是dfs写少了,卡到写不出来,后来被yj先写出来了,djk写了个我们俩讨论的一个奇怪的做法,也都a了,蚂蚁题没思路也不想写,再加上yj有事,我们就索性3小时下班了…

你可能感兴趣的:(团队训练赛,ICPC,算法)