题解:CF755F PolandBall and Gifts
_saltFish_ · · 题解
简要题意
给出若干个有向环,共
思路
按照送礼顺序连边能得到很多个有向环。一个人没带礼物相当于删除这个与点相连的所有边,答案就是删除的边的数量。最小化当然是尽量让同一个环中的人不带,这样可以使删除的边尽量重复。最多只会有
优化求最小答案的过程。瓶颈在于 0-1 背包,貌似可以直接考虑分组背包。设大小不同的环有
code
丑陋的考场代码,轻喷。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n, k, fa[N], siz[N], num[N], tot, w[N];
bool used[N], f[N];
int fnd(int x) {
if(fa[x] == x) {
return x;
}
return fa[x] = fnd(fa[x]);
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int _T = 1;//考场上有多测
while(_T--) {
cin >> n >> k;
for(int i = 1; i <= n; i++) {
siz[i] = 1;
fa[i] = i;
used[i] = 0;
f[i] = 0;
num[i] = 0;
}
for(int i = 1; i <= n; i++) {
int p;
cin >> p;
if(fnd(p) != fnd(i)) {
siz[fnd(p)] += siz[fnd(i)];
fa[fnd(i)] = fnd(p);
}
}
f[0] = 1;
int ans = 0, now = k, cnt = 0;
for(int i = 1; i <= n; i++) {
if(!used[fnd(i)]) {
used[fnd(i)] = 1;
int x = siz[fnd(i)];
// for(int j = k; j - x >= 0; j--) {//70pts
// f[j] |= f[j - x];
// }
num[x]++;
int can = min(now, siz[fnd(i)] / 2);
ans += can * 2;
now -= can;
if(siz[fnd(i)] % 2 == 1) {
cnt++;
}
}
}
ans += min(now, cnt);
tot = 0;
for(int i = 1; i <= n; i++) {//mlogn
if(num[i]) {//m
for(int j = 1; j <= num[i]; j <<= 1) {//logn
w[++tot] = i * j;
num[i] -= j;
}
if(num[i]) {
w[++tot] = i * num[i];
}
}
}
// cout << tot << ' ';
for(int i = 1; i <= tot; i++) {//mklogm 100pts
for(int j = k; j - w[i] >= 0; j--) {
f[j] |= f[j - w[i]];
}
}
if(f[k]) {
cout << k << ' ';
} else {
cout << k + 1 << ' ';
}
cout << ans << '\n';
}
}