Atcoder 235 题解

AtCoder Beginner Contest 235

C - The Kth Time Query
题意:

​ 现有包含 N 个的整数序列 A 以及 Q 次查询,每次查询从序列 A 中找到 x i x_i xi k i k_i ki 的位置,若查询不到,则输出 -1。

题解:

​ 知识点:排序加二分。 将原下标及 a i a_i ai封装结构体进行排序,键值为 a i a_i ai,二分查找 x i x_i xi 所出现的第一个位置及出现的最后一个位置加一位置,即lower_bound, upper_bound,若在序列 A 中未查询到 a i a_i ai ,则直接特判输出 - 1,否则将二分得到的区间与 k i k_i ki 相比较,超出范围则特判输出 - 1,则直接输出特定位置。

代码:
#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 7;

struct Node {
    int pos, val;
    bool operator<(const Node &rhs) const{
        if (val == rhs.val)
            return pos < rhs.pos;
        else 
            return val < rhs.val;
    }
} a[N];

int n, q;

int lower_find(int x)
{
    int l = 1, r = n + 1;
    while (l < r) {
        int mid = (l + r) >> 1;
        if (x > a[mid].val)
            l = mid + 1;
        else 
            r = mid;
    }
    
    if (a[l].val == x)
        return l;
    else
        return 0;
}

int upper_find(int x)
{
    int l = 1, r = n + 1;
    while (l < r) {
        int mid = (l + r) >> 1;
        if (x >= a[mid].val)
            l = mid + 1;
        else 
            r = mid;
    }

    if (a[l - 1].val == x)
        return l;
    else 
        return 0;
}

int main()
{
    ios::sync_with_stdio(0);
    cin >> n >> q;
    for (int i = 1; i <= n; i ++) {
        cin >> a[i].val;
        a[i].pos = i;
    }

    sort (a + 1, a + n + 1);

    int x, k;
    vector<int> ans;
    for (int i = 1; i <= q; i ++) {
        cin >> x >> k;

        int pos_l = lower_find(x);
        int pos_r = upper_find(x);

        if (!pos_l || !pos_r)
            ans.push_back(-1);
        else if (pos_l + k - 1 >= pos_r)
            ans.push_back(-1);
        else 
            ans.push_back(a[pos_l + k - 1].pos);
    }

    for (auto &it : ans)
        cout << it << endl;
    return 0;
}
D - Multiply and Rotate
题意:

​ 给定初始值为 x = 1, 两种操作方式,第一种是将 x 替换为 a * x, 第二种是将 x 视为字符串,当前仅当满足 x 大于等于 10,x 不能被 10 整除, 将 x 的最右端字符放到最左端。求解 x = N 时的最小操作次数。

题解:

​ 知识点:广度优先搜索。此题与三个杯子倒水问题相似,观察到 N 的范围只有 1e6 ,得知此题可用BFS来做,将 x 视作一个状态,不断的根据两个操作进行拓展即可。

代码:
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 1e6 + 7;

int a, n;

int level[N];

void BFS()
{
    int array_int[10];
    bool vis[N];
    memset(vis, 0, sizeof(vis));
    queue<int> q;


    q.push(1);
    level[1] = 0;
    while (!q.empty()) {
        int u = q.front(); q.pop();
        if (vis[u]) continue;
        vis[u] = true;

        if (u == n)
            break;

        if (1ll * a * u <= N) {
            int v_int = a * u;
            q.push(v_int);
            level[v_int] = level[u] + 1;
        }
        
        if (u > 10 && u % 10) {
            int m = 0, v_int = 0, tmp = u;
            while (tmp) {
                array_int[m ++] = tmp % 10;        
                tmp /= 10;
            }
            array_int[m] = array_int[0];
            for (int i = m; i >= 1; i --)
                v_int = 10 * v_int + array_int[i];
            
            q.push(v_int);
            level[v_int] = level[u] + 1;
        }
    }
}

int main()
{
    ios::sync_with_stdio(0);
    cin >> a >> n;

    BFS();

    if (level[n])
        cout << level[n] << endl;
    else 
        cout << "-1" << endl;
    return 0;
}
E - MST + 1
题意:

​ 给定一张带有边权的无向图,有自环重边,现有 Q 次询问给出三元组(u, v, w),意为从 u->v 边权为 w,对于每次查询将该边直接放入给定无向图中,判断生成的最小生成树中是否出现查询边。

题解:

​ 知识点:克鲁斯卡尔算法。此题时克鲁斯卡尔算法转换思路,对于 Q 次查询每次都跑一边 Kruskal 时间复杂度为 O ( q   n   l o g n ) O(q\ n\ logn) O(q n logn) ,观察得在使用 Kruskal 建立最小生成树时有很多重复步骤,现可以直接将查询边放入至原图中直接跑 Kruskal ,但并不参与实际建树过程,未在同一连通分量中的点进行判断是否为查询边,若是则加入答案集合,否则直接连边。

代码:
#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 7;

struct Node {
    int from, to, val, pos;
    bool tag;
    bool operator<(const Node& rhs) const {
        return val < rhs.val;
    }
};

int n, m, q;

int f[N];

vector<Node> e;
set<int> se;

int find(int x)
{
    return f[x] == x ? x : f[x] = find(f[x]);
}

void Kruskal()
{
    for (int i = 1; i <= n; i ++)
        f[i] = i;

    int edge_size = e.size();
    for (int i = 0; i < edge_size; i ++) {
        int f1 = find(e[i].from), f2 = find(e[i].to);
        if (f1 == f2)
            continue;
        
        if (e[i].tag) {
            se.insert(e[i].pos);
        } else {
            f[f1] = f2;
        }
    }
}

int main()
{
    ios::sync_with_stdio(0);
    cin >> n >> m >> q;
    int x, y, z;
    for (int i = 1; i <= m; i ++) {
        cin >> x >> y >> z;
        e.push_back({x, y, z, 0, false});
    }

    for (int i = 1; i <= q; i ++) {
        cin >> x >> y >> z;
        e.push_back({x, y, z, i, true});
    }

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

    Kruskal();

    set<int>::iterator it;
    for (int i = 1; i <= q; i ++) {
        it = se.find(i);
        if (it == se.end())
            cout << "No" << endl;
        else 
            cout << "Yes" << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值