Codeforces Round #623 (Div. 2) D.Recommendations 并查集

ABC实在是没什么好说的,但是D题真的太妙了,详细的说一下吧

首先思路是对于a相等的分类,假设有n个,则肯定要把n-1个都增加,因为a都是相等的,所以肯定是增加t小的分类,也就是说每次都能处理一个分类,复杂度是O(n^2),这个思路很好写,优先队列随便搞一下就行了,但是题目中N = 2 * 1e5,n^2肯定是不行的,然后就是这个很巧妙的方法了,cf官方的题解我没看懂...有点尴尬,它也没给标程,但是cf的题解好像不是这个方法

看了网上用并查集的方法,首先以t为关键字从大到小排序,然后用并查集维护当前这个应该加到哪个数字,因为是从大到小排序,所以加的数字越来越大,假设排序后是1112,首先第一次是1,不用加,把1连到2,第二次把1连到3,第三次把1连到4,然后最妙的事情就是,2这时候因为1连了2,所以这时候我们可以知道2需要连到4,因为每个数字一层一层的往上加,在合并的过程中就合并在一起了,所以保证了正确性。

#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;
const int N = 200010;
struct news{
    int a, t;
    bool operator < (const news &o) const{
        return t > o.t;
    }
} num[N];
map < int, int > fa;

int find(int a) {
    return (fa[a] == 0) ? a : (fa[a] = find(fa[a]));
} 

inline void merge(int a, int b) {
    int f = find(b);
    if (f != a)
        fa[a] = f;
}
int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", &num[i].a);
    for (int i = 0; i < n; i++)
        scanf("%d", &num[i].t);
    long long ans = 0;
    sort(num, num + n);
    for (int i = 0; i < n; i++) {
        int f = find(num[i].a);
        if (f == num[i].a) {
            merge(f, f + 1);
        }
        else {
            merge(f, f + 1);
            ans = ans + 1ll * (f - num[i].a) * num[i].t;
        }
    }
    printf("%lld
", ans);
    return 0;
}

------------恢复内容结束------------

原文地址:https://www.cnblogs.com/cminus/p/12368054.html