The 3rd Universal Cup. Stage 29: Metropolis(2024上海区域赛ICBGD)

I. In Search of the Ultimate Artifact

分析:签到,要注意我们需要排序,再找到最先为0的位置这个k -1大小的区间就不能选了

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

const int mod = 998244353;

// 0 0 1 2 3 4 5 6

void solve()
{
    int n, k;
    cin >> n >> k;
    vector<ll> a(n);
    for (int i = 0; i < n; i++)
        cin >> a[i];
    sort(a.begin(), a.end(), greater<int>());
    int d0 = n;
    ll ans = a[0] % mod;
    for (int i = 0; i < n; i++)
    {
        if (a[i] == 0)
        {
            d0 = i;
            break;
        }
    }
    // 每次可以找 k -1个 看一共可以找多少个 k - 1
    // cout << "sum * ( k - 1) = " << endl;

    int sum = (d0 - 1) / (k - 1);
    for (int i = 1; i <= sum * (k - 1); i++)
    {
        ans = ans * a[i] % mod;
    }

    cout << ans << endl;
}

int main()
{
    int t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

C. Conquer the Multiples

分析:注意到拿奇数的人可能去阻断拿偶数的人,而偶数的人不可能阻断自己,分类一下下就可以

#include <bits/stdc++.h>

using namespace std;

int main()
{

    int t;
    cin >> t;
    while (t--)
    {
        int l, r;
        cin >> l >> r;
        // l l + 1 l + 2 , ...
        // l   l + 1   l + 2   l + 3
        // 最先阻断对方的是胜利者
        // 偶数的一方不会去跳跃拿
        // 奇数的一方会跳跃拿 跳跃 2倍 或者跳跃4倍 偶数倍 但实际上只要能跳偶数这个人就必输
        if (l % 2 == 1)
        {
            if (l * 2 <= r)
            {
                cout << "Alice" << endl;
            }
            else
            {
                if (r % 2 == 1)
                    cout << "Alice" << endl;
                else
                    cout << "Bob" << endl;
            }
        }
        else
        {
            if ((l + 1) * 2 <= r - (r % 2))
            {
                cout << "Bob" << endl;
            }
            else
            {
                if (r % 2 == 1)
                    cout << "Bob" << endl;
                else
                    cout << "Alice" << endl;
            }
        }
    }

    return 0;
}

B. Basic Graph Algorithm

分析:手速可以铜牌的题,本身不难,但是要注意很多细节,以及必须按照dfs的顺序进行,不能手动模拟。

#include <bits/stdc++.h>
using namespace std;
int n, m;
int arr[300005], nex = 1;
vector<int> vct[300005];
set<int> st[300005];
vector<pair<int, int>> ans;

void dfs(int u)
{
    // 这里会有一个非常非常不好发现的错误!!如果没有vct再存一次图,只是用set存图的话.
    // 一旦x在y前先遍历,那么y会删除到x这条边,但是x不会删除到y这条边(因为y到x已经没边了).
    // 如果此时y不是被x遍历到的,那么将可能会多建边.
    // 即x应该要return才对的,但是x没有return,因为还存有到y的边,但是实际上y已经被遍历完了.
    for (auto v : vct[u])
        st[v].erase(u); // 因为这个点已经遍历过了 所有想走到它的边都必须删除
    while (!st[u].empty() && nex <= n)
    {
        if (st[u].count(arr[nex]))
        {
            // st[u].erase(arr[nex]);
            nex++, dfs(arr[nex - 1]);
        }
        else
        {
            ans.emplace_back(u, arr[nex]);
            nex++, dfs(arr[nex - 1]);
        }
    }
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int u, v;
        cin >> u >> v;
        // 无自环,有重边.
        // 需要再建一个不做删减的图,用作给st删边.如果单纯用st作为删边的话,会有问题!!并且很难发现这个问题..
        /*if(!st[u].count(v))*/
        vct[u].emplace_back(v), vct[v].emplace_back(u);
        st[u].emplace(v), st[v].emplace(u);
    }
    for (int i = 1; i <= n; i++)
        cin >> arr[i];
    while (nex <= n)
        nex++, dfs(arr[nex - 1]);

    cout << ans.size() << endl;
    for (auto a : ans)
        cout << a.first << " " << a.second << endl;
    return 0;
}

G. Geometry Task

分析:根据数据范围注意到是一个二分的题目,但是要贪心进行选蓝的坐标,我们可以找到满足m的需要的x坐标,对于正斜率的和负斜率的分别存起来,排序之后再贪心的选择,负斜率的x坐标从小到大排序,可以实现先满足苛刻的负斜率,把蓝的坐标作用最大化,正斜率的x坐标从大到小排序,可以实现先满足更苛刻的正斜率(因为rx是从右边开始移动的),最后我们判断满足的数量 return num >= (n + 1) / 2;。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ld long double
const int N = 1e5 + 10 , INF = 2e18;

struct{
    int a, b;
} l[N];

void solve(){
    int n;
    cin >> n ;
    for (int i = 0; i < n; i++ )
        cin >> l[i].a;
    for (int i = 0; i< n ; i ++)
        cin >> l[i].b;
    vector<int> c(n);
    for (int i = 0; i < n;i ++ )
        cin >> c[i];

    sort(c.begin(), c.end());

    auto check = [&](ld m) -> bool
    {
        int num = 0; // 满足要求的线的数量 如果是水平的就得直接去判断可不可以满足要求
        vector<int> lt, rt;//分别存正斜率和负斜率需要的x坐标的界限
        for (int i = 0; i<n ; i ++)
        {
            ld a = l[i].a, b = l[i].b;
            if(a == 0 ){
                if(l[i].b >= m)
                    num++;
                continue;
            }
            if( a > 0 ){
                // 一个正斜率的线要满足大于m x坐标至少是 (m - b) / a 上取整
                int nd_x = ceil((m - b ) / a);
                rt.push_back(nd_x);
            }
            if(  a < 0){
                // 一个负斜率的线要满足大于m x的坐标至多是 ( m - b ) / a下取整 负数除法
                int nd_x = floor((ld)(m - b) / (a));
                lt.push_back(nd_x);
            }
        }
        // 根据搜索方向不同 排序的方向也不同
        sort(lt.begin(), lt.end());
        sort(rt.begin(), rt.end(), greater<int>());
        int lx = 0, rx = n - 1; // 分别是开始搜索的方向
        vector<int> vis(n, 0);
        for (auto &i : lt)
        {
            // 先满足小的
            if (c[lx] <= i && lx < n )
            {
                vis[lx] = 1;
                lx++;
                num++;
            }
        }
        for (auto &i : rt)
        {
            if (c[rx] >= i && !vis[rx]  && rx >= 0)
            {
                num++;
                rx--;
            }
        }
        return num >= (n + 1) / 2;
    };

    int l = -INF, r = INF;

    while(l <= r){
        int mid = l + r >> 1;
        if(check(mid)) l = mid + 1;
        else
            r = mid - 1;
    }
    cout << r << endl;
}

signed main(){
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t;
    cin >> t;
    while( t -- )
        solve();
    return 0;
}

D. Decrease and Swap

分析:这个题目就很头大了,如果运气好注意到我们只关心最后两位能不能 消掉,就可以尽可能的去把1匀到右边去,比如1111000101就可以匀成1010101110,然后去判断后五位的情况:

  • 具体情况分析 (Case Analysis)

    • 倒数三、四位是 11 (...11xx): 这种情况总是有解的。因为 s[n-4]s[n-3] 的 '1' 提供了足够多的交换机会,可以灵活地处理 s[n-2]s[n-1] 的情况,最终都能把它们变成 '0'。

    • 倒数三、四位是 00 (...00xx): 这种情况总是无解的。因为缺少了 s[n-4]s[n-3] 的操作能力,末尾的 '1' 会被“锁死”。

    • 倒数三、四位是 10 (...10xx): 只能解决一些特殊情况。s[n-4] 的 '1' 提供了一次交换 s[n-3]s[n-2] 的机会,能否成功取决于 s[n-2]s[n-1] 的具体值。

    • 倒数三、四位是 01 (...01xx): 能否解决取决于倒数第五位。如果倒数第五位是 '1',可以通过操作将其变为 ...10 的情况,从而可能解决问题;如果倒数第五位是 '0',则无法操作,问题无解。

#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
const int N = 1e6 + 10;

void solve()
{
    int n;
    cin >> n;
    vector<char> s(n);
    for (auto &i : s)
        cin >> i;

    if (s[n - 1] == '0' && s[n - 2] == '0')
    {
        cout << "Yes" << endl;
        return;
    }
    if (n == 3)
    {
        cout << "No" << endl;
        return;
    }

    for (int l = 0; l < n - 2; l++)
    {
        if (s[l] != '1')
            continue;
        int cnt = 1;
        int r = l + 1; // [l , r) 这样的一个区间尽可能去扩展
        while (r < n - 1 && (r - l) <= 2 * cnt)
        {
            if (s[r] == '1')
                cnt++;
            r++;
        }
        // 先全部清空
        for (int i = l; i < r; i++)
            s[i] = '0';
        for (int i = l; i < r; i+=2)
        {
            s[i] = '1';
            cnt--;
            if(cnt == 0 )
                break;
        }
        // 最后的cnt可能仍然有剩余
        for (int i = r - 1; i >= l; i--)
        {
            if (cnt == 0)
                break;
            if (s[i] == '0')
            {
                s[i] = '1';
                cnt--;
            }
        }
        l = r - 1;
    }
    if (s[n - 2] == ' 0' && s[n - 1] == '0' || s[n - 4] == '1' && s[n - 3] == '1')
        cout << "Yes" << endl;
    else if (s[n - 4] == '0' && s[n - 3] == '0')
        cout << "No" << endl;
    
    else if (s[n - 4] == '1' && s[n - 3] == '0')
    {
        if (s[n - 2] == '0' && s[n - 1] == '1')
            cout << "No" << endl;
        else
            cout << "Yes" << endl;
    }
    else if (n >= 5 && s[n - 5] == '1')
        cout << "Yes" << endl;
    else 
        cout << "No" << endl;
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t;
    cin >> t;
    while (t--)
        solve();

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值