2020牛客暑期多校训练营(第八场)
目录
Contest Info
Solved | A | B | C | D | E | F | G | H | I | J | K |
---|---|---|---|---|---|---|---|---|---|---|---|
5 / 11 | O | - | - | - | O | - | O | - | O | - | O |
- O 在比赛中通过
- Ø 赛后通过
- ! 尝试了但是失败了
- - 没有尝试
Solutions
A. All-Star Game
对时间分治,线段树维护可撤销并查集的模板题。
这样可以直接维护每一个时刻的连通性,我们只需要求出当前连通块的个数、双方孤立的点数即可得出答案。
所以在增、添边时维护一下点的度数和孤立点数即可。
Code
// Author : heyuhhh
// Created Time : 2020/08/04 09:12:36
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
void err(int x) {cerr << x;}
void err(long long x) {cerr << x;}
void err(double x) {cerr << x;}
void err(char x) {cerr << '"' << x << '"';}
void err(const string &x) {cerr << '"' << x << '"';}
void _print() {cerr << "]\n";}
template<typename T, typename V>
void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
template<typename T>
void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
template <typename T, typename... V>
void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifdef Local
#define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define dbg(x...)
#endif
//head
const int N = 4e5 + 5;
int n, m, q;
int num, cnt, blosz;
int d[N];
struct UFS {
int f[N], h[N], sz[N], top;
struct node {
int x, y, fx, h, SZ, X, Y;
}sta[N];
void init() {
top = 0;
for (int i = 1; i <= n + m; i++) {
sz[i] = (i > m ? 1 : 0);
f[i] = i;
h[i] = 0;
}
}
int find(int x) {
return f[x] == x ? f[x] : find(f[x]);
}
bool merge(int u, int v) {
int x = find(u), y = find(v);
if (x == y) return false;
if (h[x] > h[y]) swap(x, y);
if (++d[u] == 1) --cnt;
if (++d[v] == 1) --num;
if (sz[x] && sz[y]) --blosz;
sta[++top] = node{x, y, f[x], h[y], sz[y], u, v};
if (h[x] == h[y]) ++h[y];
sz[y] += sz[x];
f[x] = y;
return true;
}
void undo(int k) {
while (k--) {
node it = sta[top--];
f[it.x] = it.fx;
h[it.y] = it.h;
sz[it.y] = it.SZ;
int u = it.X, v = it.Y;
if (--d[u] == 0) ++cnt;
if (--d[v] == 0) ++num;
if (sz[it.y] && sz[it.x]) ++blosz;
}
}
int query(int x) {
int fx = find(x);
return sz[fx];
}
} ufs;
vector<pii> nodes[N << 2];
int ans[N];
void add(int o, int l, int r, int L, int R, pii v) {
if (L <= l && r <= R) {
nodes[o].push_back(v);
return;
}
int mid = (l + r) >> 1;
if (L <= mid) {
add(o << 1, l, mid, L, R, v);
}
if (R > mid) {
add(o << 1|1, mid + 1, r, L, R, v);
}
}
void dfs(int o, int l, int r) {
int cc = 0;
for (auto& it : nodes[o]) {
int fans = it.fi, player = it.se;
if (ufs.merge(fans, player + m)) ++cc;
}
if (l == r) {
dbg(l, num, blosz);
if (cnt > 0) ans[l] = -1;
else ans[l] = blosz - num;
ufs.undo(cc);
return;
}
int mid = (l + r) >> 1;
dfs(o << 1, l, mid);
dfs(o << 1|1, mid + 1, r);
ufs.undo(cc);
}
void run() {
cin >> n >> m >> q;
ufs.init();
map<pii, int> S;
for (int i = 1; i <= n; i++) {
int k;
cin >> k;
for (int j = 1; j <= k; j++) {
int x;
cin >> x;
S[MP(x, i)] = 0;
}
}
for (int _ = 1; _ <= q; _++) {
int x, y;
cin >> x >> y;
if (S.count(MP(x, y))) {
add(1, 0, q, S[MP(x, y)], _ - 1, MP(x, y));
S.erase(MP(x, y));
} else {
S[MP(x, y)] = _;
}
}
for (auto& it : S) {
add(1, 0, q, it.se, q, it.fi);
}
num = n;
cnt = m;
blosz = n;
dfs(1, 0, q);
for (int i = 1; i <= q; i++) {
cout << ans[i] << '\n';
}
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
E. Enigmatic Partition
假设我们知道了首项\(x\)以及项数\(m\),那么我们会发现会对在区间\([(m-2)x+2x+3,(m-2)(x+2)+2x+1]\)即\([mx+3,mx+2m-3]\)的答案尝试贡献。
然后观察有两点:
- 区间长度为奇数;
- 对每个点的答案贡献不一样。
接下来就处理第二点。
稍微手模一下就会发现我们可以对区间按照长度对\(4\)取余进行分类,两类的贡献会有不同。
对于模\(4\)为1的区间长度,比如\(1,5,9...\),他们的贡献类似于\(1,1,2,2,\cdots,2,2,1,1\)这样对称的,中间会有个最大值;对于另外一类区间贡献为\(1,1,2,2,\cdots,t,t,t,\cdots,2,2,1,1\),中间会有三个连续的最大值。
所以根据这个差分一下就行。
代码中我用的线段树维护奇、偶位置的差分,复杂度是两个log的(枚举\(x,m\)的复杂度是一个log,是类似于调和级数的,因为是\(m\cdot x\))。
代码如下:
Code
// Author : heyuhhh
// Created Time : 2020/08/03 13:00:26
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
const int n = 100001;
ll ans[N];
struct segment_tree {
ll flag[N << 2];
void tag(int o, int v) {
flag[o] += v;
}
void update(int o, int l, int r, int L, int R, int v) {
R = min(R, n);
if (L > R) return;
if(L <= l && r <= R) {
tag(o, v);
return;
}
int mid = (l + r) >> 1;
if(L <= mid) update(o << 1, l, mid, L, R, v);
if(R > mid) update(o << 1|1, mid + 1, r, L, R, v);
}
ll res;
void query(int o, int l, int r, int p) {
r = min(r, n);
if (l > r) return;
res += flag[o];
if (l == r) return;
int mid = (l + r) >> 1;
if (p <= mid) query(o << 1, l, mid, p);
else query(o << 1|1, mid + 1, r, p);
}
ll query(int p) {
res = 0;
query(1, 1, n, p);
return res;
}
}A, B;
void run() {
for (int x = 1; x <= n; ++x) {
for (int m = 3; m <= n; ++m) {
ll L = 1ll * m * x + 3, R = 1ll * m * (x + 2) - 3;
if (L > (ll)100000) break;
int mid = (L + R) >> 1;
if ((R - L + 1) % 4 == 3) {
if (L & 1) {
B.update(1, 1, n, L, mid, 1);
A.update(1, 1, n, mid + 2, R + 1, -1);
} else {
A.update(1, 1, n, L, mid, 1);
B.update(1, 1, n, mid + 2, R + 1, -1);
}
} else {
if (L & 1) {
B.update(1, 1, n, L, mid, 1);
A.update(1, 1, n, mid + 1, R + 1, -1);
} else {
A.update(1, 1, n, L, mid, 1);
B.update(1, 1, n, mid + 1, R + 1, -1);
}
}
}
}
for (int i = 1; i < n; i++) {
ans[i] = (i & 1 ? B.query(i) : A.query(i));
}
for (int i = 1; i < n; i++) {
ans[i] += ans[i - 1];
}
for (int i = 1; i < n; i++) {
ans[i] += ans[i - 1];
}
int T;
cin >> T;
int _ = 0;
while (T--) {
++_;
int l, r;
cin >> l >> r;
cout << "Case #" << _ << ": ";
cout << ans[r] - ans[l - 1] << '\n';
}
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
G. Game SET
直接暴力冲就行。
I. Interesting Computer Game
建图后对每个连通块判断是否存在环即可。
Code
// Author : heyuhhh
// Created Time : 2020/08/03 12:14:49
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
int _;
void run() {
++_;
int n;
cin >> n;
map<int, int> mp;
vector<vector<int>> G(2 * n);
int tot = 0;
vector<int> d(2 * n);
for (int i = 1; i <= n; i++) {
int u, v;
cin >> u >> v;
if (mp.find(u) == mp.end()) {
mp[u] = tot++;
}
if (mp.find(v) == mp.end()) {
mp[v] = tot++;
}
G[mp[u]].push_back(mp[v]);
G[mp[v]].push_back(mp[u]);
++d[mp[u]], ++d[mp[v]];
}
vector<bool> vis(tot);
int sum = 0, cnt = 0;
function <void(int)> dfs = [&] (int u) {
vis[u] = true;
sum += d[u];
++cnt;
for (auto v : G[u]) {
if (!vis[v]) {
dfs(v);
}
}
};
int ans = 0;
for (int i = 0; i < tot; i++) {
if (!vis[i]) {
cnt = sum = 0;
dfs(i);
sum /= 2;
if (sum < cnt) {
ans += sum;
} else {
ans += cnt;
}
}
}
cout << "Case #" << _ << ": ";
cout << ans << '\n';
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T; while(T--)
run();
return 0;
}
K. Kabaleo Lite
直接贪心,但注意答案可能是\(10^{19}\)会爆long long,然后施展各种奇技淫巧就行。
重要的是自信,一旦有了自信,人就会赢得一切。