AtCoder Beginner Contest 282(D-F)

D - Make Bipartite 2 (atcoder.jp)

        (1)题目大意

                给你一个无向图,问你要添加多少条边可以使这个图变成一个二分图。

         (2)解题思路

                因为是无向图,我们考虑并查集,若给出的边有u->v,考虑条件并查集(扩展域并查集)设u为选择u点,u+n为不选择u点,若u->v,则merge(u,v + n),merge(v,u + n)。因为这样会牵扯进来2n个点,因此答案要除以2,又因为题目已经给了m条边,因此答案/2后还要减m。

        (3)代码实现

#include "bits/stdc++.h"
#define rep(i, z, n) for (int i = z; i <= n; i++)
#define per(i, n, z) for (int i = n; i >= z; i--)
#define ll long long
#define db double
#define PII pair<int, int>
#define fi first
#define se second
#define vi vector<int>
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
using namespace std;
const int N = 4e5 + 10;
int f[N], cnt[N];
int find(int x)
{
    return x == f[x] ? x : f[x] = find(f[x]);
}
void merge(int x, int y)
{
    int fx = find(x), fy = find(y);
    f[fx] = fy;
}
void solve()
{
    int n, m;
    iota(f, f + N, 0);
    cin >> n >> m;
    rep(i, 1, m)
    {
        int u, v;
        cin >> u >> v;
        if (find(u) == find(v))
        {
            cout << 0 << endl;
            return;
        }
        merge(u, v + n);
        merge(v, u + n);
    }
    rep(i, 1, n)
    {
        cnt[find(i)]++;
    }
    ll ans = 0;
    rep(i, 1, n)
    {
        ans += n - cnt[find(i)];
    }
    ans /= 2;
    ans -= m;
    cout << ans << endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int T = 1;
    // cin >> T;
    while (T--)
        solve();
    return 0;
}

E - Choose Two and Eat One (atcoder.jp)

        (1)题目大意

                给你一个序列,你每次可以选择两个值x和y,他们的贡献是(x^y + y^x) mod m,最后问你在选n-1个的情况下最大能获得多少?

         (2)解题思路

                考虑预处理出每两个球的贡献,然后排序,最后跑一个最大生成树即可。

        (3)代码实现

// Problem: E - Choose Two and Eat One
// Contest: AtCoder - HHKB Programming Contest 2022 Winter(AtCoder Beginner Contest 282)
// URL: https://atcoder.jp/contests/abc282/tasks/abc282_e
// Memory Limit: 1024 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include "bits/stdc++.h"
#define rep(i, z, n) for (int i = z; i <= n; i++)
#define per(i, n, z) for (int i = n; i >= z; i--)
#define ll long long
#define db double
#define PII pair<int, int>
#define fi first
#define se second
#define vi vector<int>
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
using namespace std;
const int N = 510;
int n, m;
int a[N], f[N * N];
struct Edge
{
    int x, y, v;
    bool operator<(const Edge &other) const
    {
        return v > other.v;
    }
};
int find(int x)
{
    return x == f[x] ? x : f[x] = find(f[x]);
}
int ksm(int a, int p)
{
    int res = 1;
    while (p)
    {
        if (p & 1)
        {
            res = 1LL * res * a % m;
        }
        a = 1LL * a * a % m;
        p >>= 1;
    }
    return res;
}
void solve()
{
    cin >> n >> m;
    rep(i, 1, n)
    {
        cin >> a[i];
    }
    rep(i, 1, n * n)
    {
        f[i] = i;
    }
    vector<Edge> e;
    rep(i, 1, n)
    {
        rep(j, i + 1, n)
        {
            int v1 = ksm(a[i], a[j]), v2 = ksm(a[j], a[i]);
            e.push_back({i, j, (v1 + v2) % m});
        }
    }
    sort(e.begin(), e.end());
    ll ans = 0;
    for (auto x : e)
    {
        int fx = find(x.x), fy = find(x.y);
        if (fx != fy)
        {
            ans += x.v;
            f[fx] = fy;
        }
    }
    cout << ans << endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int T = 1;
    // cin >> T;
    while (T--)
        solve();
    return 0;
}

F - Union of Two Sets (atcoder.jp)

        (1)题目大意

                这是一个交互题,首先给你一个n,然后让你给出M组[l,r],最后给你q个询问,问你给出的M组[l,r]中,包含了[L,R]的区间的给出顺序[l,r]。

         (2)解题思路

                这个题一眼看去是类似于ST表的构造,因此我们考虑一下ST表的构造方式,用vis[i][j],表示从i往后跳1>>j -1,那么此时由于n只有4000,因此直接用vis[i][j]表示从i->j,我们用这些来构造区间即可,又因为ST表构造是nlog2n的,因此构造的区间数量最多是4000*log2(4000) 不超过44000,因此此法可行。

        (3)代码实现

#include "bits/stdc++.h"
#define rep(i, z, n) for (int i = z; i <= n; i++)
#define per(i, n, z) for (int i = n; i >= z; i--)
#define ll long long
#define db double
#define PII pair<int, int>
#define fi first
#define se second
#define vi vector<int>
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
using namespace std;
const int N = 4e3 + 10;
int vis[N][N];
void solve()
{
    int n;
    cin >> n;
    vector<PII> v;
    for (int j = 1; j <= n; j++)
    {
        for (int i = 1; i + j - 1 <= n; i *= 2)
        {
            v.push_back({j, j + i - 1});
            vis[j][j + i - 1] = v.size();
        }
    }
    cout << v.size() << endl;
    for (auto x : v)
    {
        cout << x.fi << ' ' << x.se << endl;
    }
    int q;
    cin >> q;
    while (q--)
    {
        int l, r;
        cin >> l >> r;
        int len = log2(r - l + 1);
        cout << vis[l][l + (1 << len) - 1] << ' ' << vis[r - (1 << len) + 1][r] << endl;
    }
}
int main()
{
    int T = 1;
    ios::sync_with_stdio(false);
    cin.tie(0);
    // cin >> T;
    while (T--)
        solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值