【题解】CF878C Tournament

版权声明:Powered By Fighter https://blog.csdn.net/qq_30115697/article/details/88650171

思路

​ 又是一道建模思维题。我们考虑两个选手之间的关系,如果一个选手能在某一项运动中战胜对手,那么就从他自身向对手连一条有向边。这样显然会出现很多环,于是可以大力缩点,将整张图缩成一个DAG(实际实现中会变为一条链)。那么显然入度为零的环中包含的点数即为最后可能成为冠军的人数。

实现细节

​ 由于题目要求动态插入点,那么tarjan就不再适合了~~(虽然我并不清楚有没有大佬能用tarjan切掉)~~,于是我们换一种方法建图。由于一位选手不可能战胜对手,当且仅当所有运动能力均弱于对方。那么我们就以这个条件来判断,如果满足这个条件就连单向边,否则就合并到一个连通块中。于是我们可以选择set作为容器,把上述判断的条件重载成小于号,在set中用find查找是否有与当前选手可以合并的,并进行合并操作。合并后,我们的节点(即一个环)记录的是环中每项运动所有选手中的最大值和最小值,这样可以方便地进行环与环之间的比较。

​ 将所有点插入后,答案即为set中的最后一个元素的大小,因为我们重载了小于号,所以最后一个节点都是“大于”之前的点的,在缩点后的图中一定是入度为零。

代码

#include <bits/stdc++.h>
using namespace std;

int n, k;

struct node{
    int sz;
    int mx[15], mn[15];
    node(){
        memset(mx, 0, sizeof(mx));
        memset(mn, 0x3f, sizeof(mn));
    }
    friend bool operator <(node a, node b){
        for(int i = 1; i <= k; i++){
            if(a.mx[i] > b.mn[i]){
                return false;
            }
        }
        return true;
    }
};

set<node> s;
set<node>::iterator it;

int main()
{
    cin >> n >> k;
    node t;
    for(int i = 1; i <= n; i++){
        t.sz = 1;
        for(int j = 1; j <= k; j++){
            scanf("%d", &t.mn[j]);
            t.mx[j] = t.mn[j];
        }
        while((it = s.find(t)) != s.end()){
            t.sz += it->sz;
            for(int j = 1; j <= k; j++){
                t.mn[j] = min(t.mn[j], it->mn[j]);
                t.mx[j] = max(t.mx[j], it->mx[j]);
            }
            s.erase(it);
        }
        s.insert(t);
        printf("%d\n", (--s.end())->sz);
    }
    
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_30115697/article/details/88650171