The 2023 ICPC Asia Shenyang Regional Contest(2023沈阳区域赛CEJK)

C. Swiss Stage

分析:签到要注意BO1 和 BO3的小字。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
#define ll long long

void solve(){
    int x ,  y;
    cin >> x >> y;
    if(x == 0 && y ==1 )
        cout << 4 << endl;
    else if( x == 1 && y == 2){
        cout << 4 << endl;
    }else if(x == 0 && y == 0){
        cout << 4 << endl;
    }else if( x == 0 && y == 2){
        cout << 6 << endl;
    }else if( x == 1 && y == 0 ){
        cout << 3 << endl;
    }else if(x == 1 && y == 1){
        cout << 3 << endl;
    }else if(x == 2 && y == 0 ){
        cout << 2 << endl;
    }else if(x == 2 && y == 1){
        cout <<  2 << endl;
    }else{
        cout << 2 << endl;
    }
}

int main(){
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	solve();
	return 0;
}

J. Graft and Transplant

分析:读题感觉和叶子节点有关,也就是非叶子节点的个数,非叶子节点的个数就等于操作的总次数,所以判断奇偶直接输出即可。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define ll long long

void solve()
{
    int n;
    cin >> n;
    vector<int> d(n + 1, 0);
    for (int i = 0; i < n - 1; i++)
    {
        int u, v;
        cin >> u >> v;
        d[u]++;
        d[v]++;
    }
    int cnt = 0;
    for (int i = 1; i <= n; i++)
        if (d[i] > 1)
            cnt++;
    if (n == 2)
    {
        cout << "Bob" << endl;
        return;
    }
    if (cnt % 2 == 0)
        cout << "Alice" << endl;
    else
        cout << "Bob" << endl;
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    solve();
    return 0;
}

E. Sheep Eat Wolves

分析:bfs求最短路并且要记录状态,不能贪心,因为没有办法模拟可能右边往左边运羊的情况。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N = 110;

// d[k][i][j] 表示船在 k岸(0为左岸, 1为右岸),左岸有 i只羊, j只狼时所需的最少趟数
int d[2][N][N]; 

// 定义一个别名ay,用于存储队列中的状态 {船的位置, 左岸羊数, 左岸狼数}
typedef array<int, 3> ay;

int main(){
    // x: 羊总数, y: 狼总数, p: 船的最大载量, q: 安全阈值
    int x, y, p, q;
    cin >> x >> y >> p >> q;

    // 初始化距离数组为无穷大 (0x3f3f3f3f 是一个常用的无穷大值)
    memset(d, 0x3f, sizeof d);

    // 设置初始状态:船在左岸(0),左岸有x只羊, y只狼,所需趟数为0
    d[0][x][y] = 0;

    // 创建BFS队列
    queue<ay> Q;
    // 将初始状态推入队列
    Q.push({0, x, y});

    // 开始广度优先搜索
    while(Q.size()){
        // 取出队首状态
        auto [id, x0, y0] = Q.front();
        Q.pop();

        // 计算右岸的动物数量
        // x1: 右岸羊数, y1: 右岸狼数
        int x1 = x - x0, y1 = y - y0;

        // 枚举本次过河船上搭载的动物组合
        // i: 船上总动物数, j: 船上羊的数量
        for (int i = 0; i <= p; i ++){ // i 代表船上总共的动物数量
            // j 代表船上羊的数量, 那么狼的数量就是 i - j
            // 还需要保证当前岸有足够的羊和狼可以上船
            for (int j = 0; j <= i; j++){
                // 船在左岸(id=0),要运走的羊(j)不能超过左岸的羊(x0)
                // 船在右岸(id=1),要运走的羊(j)不能超过右岸的羊(x1)
                bool enough_sheep = (id == 0 && j <= x0) || (id == 1 && j <= x1);
                // 船在左岸(id=0),要运走的狼(i-j)不能超过左岸的狼(y0)
                // 船在右岸(id=1),要运走的狼(i-j)不能超过右岸的狼(y1)
                bool enough_wolves = (id == 0 && i - j <= y0) || (id == 1 && i - j <= y1);
                
                if (!enough_sheep || !enough_wolves) continue;

                // 计算移动后的新状态
                // l_x0, l_y0: 移动后左岸的羊和狼的数量
                int l_x0 = x0 + (id ? j : -j);       // 如果船从右(1)来,左岸羊增加j;从左(0)走,减少j
                int l_y0 = y0 + (id ? (i - j) : -(i-j)); // 如果船从右(1)来,左岸狼增加i-j;从左(0)走,减少i-j

                // r_x1, r_y1: 移动后右岸的羊和狼的数量
                int r_x1 = x - l_x0;
                int r_y1 = y - l_y0;
                
                // 检查移动后的状态是否安全
                // 农民到了对岸,所以无人看管的是出发的那个岸
                bool is_safe = true;
                if (id == 0) { // 船从左岸出发,左岸无人看管
                    // 如果左岸有羊(l_x0 > 0),则必须满足狼数 <= 羊数 + q
                    if (l_x0 > 0 && l_y0 > l_x0 + q) is_safe = false;
                } else { // 船从右岸出发,右岸无人看管
                    // 如果右岸有羊(r_x1 > 0),则必须满足狼数 <= 羊数 + q
                    if (r_x1 > 0 && r_y1 > r_x1 + q) is_safe = false;
                }

                // 如果状态安全,并且找到了更短的路径
                if (is_safe && d[id ^ 1][l_x0][l_y0] > d[id][x0][y0] + 1)
                {
                    // 更新最短趟数
                    d[id ^ 1][l_x0][l_y0] = d[id][x0][y0] + 1;
                    // 将新状态推入队列
                    Q.push({id ^ 1, l_x0, l_y0});
                }
            }
        }
    }
    
    // 搜索结束后,寻找最终答案
    int ans = 1e9; // 1e9 约等于无穷大
    // 目标状态是:所有羊都在右岸(即左岸羊数为0),船在右岸(1)
    // 此时左岸狼的数量可以是任意值,所以遍历所有可能
    for (int i = 0; i <= y; i ++)
        ans = min(ans, d[1][0][i]);

    // 如果ans没有被更新过,说明无法达到目标状态
    cout << (ans >= 1e9 ? -1 : ans) << endl;
    return 0;
}

K. Maximum Rating

分析:权值线段树算是比较板的题目了。

核心就是知道正数的个数一定是可以的,这是一种情况,然后我们想知道有负数可以抵消多少次正数,最容易抵消的一次增加的可能就是 |负数和| > 最小的整数 , 其次就是 |负数和| > 最小的前两个整数 ... 其次类推 , 能抵消几次结果就是 1 + 抵消的次数。 因此我们可以直接知道数组的和是sum,然后利用权值线段树可以快速求出数组中最少需要多少个数的和>=sum,算出来的结果是c (这个c一定是某几个最大的正数的和, 剩下的整数就是会被抵消掉的正数和,|负数和| >= 没有选的正数), 正数的个数 - c + 1就是可以被抵消的正数和,也就是最后的结果。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define ll long long

const ll N = 200010, INF = 1e9;

struct SegTree
{
    ll l, r, cnt, sum;
} tr[N << 5];

ll tot;

void update(ll &u, ll l, ll r, ll d, ll cnt)    // d:需要加入的数  cnt:需要加入的数的个数
{
    if (!u)                 // 动态开点
        u = ++tot;
    tr[u].cnt += cnt;       // 类似于pushup的作用
    tr[u].sum += d * cnt;
    if (l == r)
        return;
    ll mid = l + r >> 1;
    if (d > mid)
    {
        update(tr[u].r, mid + 1, r, d, cnt);
    }
    else
    {
        update(tr[u].l, l, mid, d, cnt);
    }
}

ll query(ll u, ll l, ll r, ll sum)
{ // 求和为 sum 需要的最少的数的数量
    if (l == r)
    {
        return (sum <= 0 ? 0 : (sum + l - 1) / l);
    }
    ll mid = l + r >> 1;
    if (tr[tr[u].r].sum >= sum)     // 因为右边存的是比较大的数,如果右边的数的和>sum,则直接在右边找
        return query(tr[u].r, mid + 1, r, sum);  
    else                            // 否则先把右边比较大的数拿走,再去左边找剩下的需要的sum
        return query(tr[u].l, l, mid, sum - tr[tr[u].r].sum) + tr[tr[u].r].cnt;
}

void solve()
{
    ll n, q;
    cin >> n >> q;
    ll rt = 0, cnt = 0, sum = 0;
    vector<ll> a(n + 1);
    for (ll i = 1; i <= n; i++)
    {
        cin >> a[i];
        if (a[i] > 0)
            update(rt, 1, INF, a[i], 1), cnt++;
        sum += a[i];
    }
    while (q--)
    {
        ll x, v;
        cin >> x >> v;
        if (a[x] > 0)
            update(rt, 1, INF, a[x], -1), cnt--;
        if (v > 0)
            update(rt, 1, INF, v, 1), cnt++;
        sum += v - a[x];
        a[x] = v;
        cout << cnt - query(1, 1, INF, sum) + 1 << endl;
        //  |负数和|  >= 没有选的正数 (query贪心选小的正数)  
        //  cnt - query(1, 1, INF, sum) 求出的就是负数的和可以抵消的最多的正数和 
        // + 1 是加上了抵消了 0 个正数的情况
    }
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    solve();
    return 0;
}
相关推荐
zh_xuan3 小时前
LeeCode92. 反转链表II
数据结构·算法·链表·leecode
2401_841495643 小时前
【数据结构】汉诺塔问题
java·数据结构·c++·python·算法·递归·
xxxxxxllllllshi3 小时前
Java 集合框架全解析:从数据结构到源码实战
java·开发语言·数据结构·面试
Q741_1473 小时前
C++ 位运算 高频面试考点 力扣137. 只出现一次的数字 II 题解 每日一题
c++·算法·leetcode·面试·位运算
天特肿瘤电场研究所4 小时前
专业的肿瘤电场疗法厂家
算法
DASXSDW4 小时前
NET性能优化-使用RecyclableBuffer取代RecyclableMemoryStream
java·算法·性能优化
kfepiza4 小时前
CAS (Compare and Swap) 笔记251007
java·算法
墨染点香4 小时前
LeetCode 刷题【103. 二叉树的锯齿形层序遍历、104. 二叉树的最大深度、105. 从前序与中序遍历序列构造二叉树】
算法·leetcode·职场和发展
啊我不会诶5 小时前
23ICPC澳门站补题
算法·深度优先·图论