首页 > 其他分享 >CF755F

CF755F

时间:2023-11-04 23:24:32浏览次数:32  
标签:那么 return 没带 int CF755F 个数 礼物

前言

随机跳题跳来的。本来以为很简单,结果花了我这个蒟蒻三个多小时。果然还是太蒻了呀。还有题目中的拖拉机是什么呀?

于是,题解记之。

题意

传送门

分析

我们容易发现,若把每个人当做一个点,那么给人送礼物,则是向这个人连一条边。

看不懂?直接上图!

图片走丢力

上图表示的是:

5 2
3 4 1 5 2

这一组数据。当然有向或无向都对这题没有影响。

根据观察,容易得出,这个图是由若干个环组成的,并且,环的个数也就是连通块的个数。

那么,我们便可以处理出环的个数,与他所包含的点的个数,也就是人的个数。

inline int dfs(int x) {//处理出环的中有多少个点。
    v[x] = 1;
    if (v[p[x]]) return 1;
    return dfs(p[x]) + 1;
}
for (int i = 1; i <= n; i++)//处理有多少个环(连通块)
        if (!v[i]) cnt[++len] = dfs(i);

最大值

最大值相对于最小值会简单一点。

我们想一想很容易得出,若是有一个人没有礼物,那么他要给的对象与他自己都没有礼物。为避免思维僵化,请读者自行证明。

那么,我们便可以分奇数环与偶数换来讨论。设环中点的数量为 $x$。

  1. 偶数个。那么就可以分成 $\frac{k}{2}$ 组,使每一组任意一个人没带,则他要给的对象与他自己都没有礼物。
  2. 奇数个。那么就可以分成 $\lfloor \frac{k}{2} \rfloor$ 组与多出来的一个人。

若 $\sum \lfloor \frac{c[i]}{2} \rfloor \ge k$,那么,答案即为 $k \times 2$。否则我们就将 $\sum \lfloor \frac{c[i]}{2} \rfloor \ge k$ 加上,然后再在奇数环中找多出来的一个数,直至加到大于 $k$ 或 人数等于 $n$ 时停止。

最小值

刚开始,我也觉得这是贪心,直至我 WA 了不知道多少次,想通了,这是个 DP !!!

在这里,给组对于贪心的 hack。

输入:

10 7
9 6 2 8 1 3 10 4 5 7

输出:

7 10

如下图。

图片走掉力

容易的,我们可以想出,尽量使一整个环内的人不带礼物。

略微证明一下:

设有一个环内都是没带礼物的人,人数为 $x$,另一个环都带了礼物,剩下的环保持不变,且人数大于一。那么这两个环内一共就有 $x$ 人没有礼物,但是当这个环有了一个人带了礼物,因为总共没带礼物的人数相同,那么两个环分别就有 $x - 1$ 与 $1$ 个人没带礼物,根据最大值那里的推论,总共就有 $x + 2$ 个人没有礼物。

那么,也就是说,我们需要使这 $len$ 个环中,抽出几个环,若他们人数总值等于 $k$,那么肯定是最少有 $k$ 个人没有礼物,若不等于有 $k + 1$ 人。请读者自行思考为什么。但是我还是会说的

根据上述内容可以得出若他们人数总值等于 $k$,那么肯定是最少有 $k$ 个人没有礼物。

若要使有 $k + 1$ 个人没有礼物,则使没有礼物的人都不得到礼物。但是,我们肯定会多出来一个人不能给没有礼物的人礼物,则有一个带了礼物的人没有送来的礼物。

有点绕,自行理解一下,当然,看不懂,是我的问题。

显然就考虑背包 DP。

for (int i = 1; i <= len; i++)
        for (int j = k; j >= cnt[i]; j--)
            f[j] |= f[j - cnt[i]];

$f[i]$ 表示能不能用若干个环组合出 $i$ 个人没带礼物。

时间复杂度 $O(n \times m)$。肯定会爆,考虑优化。

我们可以知道大多数的环中的人的数量是相同的。那么这题就转化成了多重背包,我们就可以用二进制优化。

inline void fen(int x, int v) {二进制优化
    for (int i = 1; x > 0; i <<= 1) {
        int j = min(i, x);
        a[++l] = j * v;
        x -= j;
    }
}

时间复杂度就降下来了。可以过。

Code

我代码写得太差了,建议大家参考其他题解的代码。

#include <bits/stdc++.h>
using namespace std;
 
const int N = 1e6 + 5;
int n, p[N], cnt[N], len, k, ans;
int c[N], l, a[N], mx, f[N * 10];//f 开 1e7 是因为二进制拆分会产生 nlogn 个数。但到不了极限。
bool v[N];
 
inline int dfs(int x) {//处理出环的中有多少个点。
    v[x] = 1;
    if (v[p[x]]) return 1;
    return dfs(p[x]) + 1;
}

inline void fen(int x, int v) {二进制优化
    for (int i = 1; x > 0; i <<= 1) {
        int j = min(i, x);
        a[++l] = j * v;
        x -= j;
    }
}
 
int main() {
    ios::sync_with_stdio(0);
    cin >> n >> k;
    for (int i = 1; i <= n; i++) cin >> p[i];
    for (int i = 1; i <= n; i++)//处理有多少个环(连通块)
        if (!v[i]) cnt[++len] = dfs(i), c[cnt[len]]++;
    sort(cnt + 1, cnt + 1 + len);//最大值需要
    for (int i = 1; i <= n; i++) if (c[i]) fen(c[i], i);
    f[0] = 1;
    for (int i = 1; i <= l; i++)
        for (int j = k; j >= a[i]; j--)
            f[j] |= f[j - a[i]];
    int kk = k;
    if (!f[k]) cout << k + 1 << ' ';
    else cout << k << ' ';//最小值
    ans = 0;
    for (int i = len; i > 0; i--) {
        if (cnt[i] <= kk * 2) {
            if (cnt[i] & 1) ans += cnt[i] - 1, kk -= cnt[i] / 2, cnt[i] = 1;
            else ans += cnt[i], kk -= cnt[i] / 2, cnt[i] = 0;
        }
        else {//排序的原因,这样就可以提前 break。
            ans += kk * 2;
            kk = 0;
            break;
        }
        if (!kk) break;
    }
    for (int i = 1; i <= len; i++) {//处理奇数环
        if (!kk) {
            break;
        }
        else {
            kk -= cnt[i];
            ans += cnt[i];
        }
    }
    cout << min(ans, n);
    return 0;
}

后记

本蒟蒻写过的最长的一篇题解吧。

在本人退役前或有空会来解答问题。

标签:那么,return,没带,int,CF755F,个数,礼物
From: https://www.cnblogs.com/luckycloud/p/17810023.html

相关文章