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;
}