100: cf 878C set+并查集+链表
$des$
Berland要举行 $n$ 次锦标赛,第一次只有一个人,之后每一次会新
加入一个人。锦标赛中有 $k$ 种运动项目,每个人在这 $k$ 种项目上都有一
个能力值,每次会选择任意两个还未被淘汰的人进行某个项目的比
赛,能力值高的人胜出,输的人被淘汰,直至只剩下一个人成为冠
军。
给出每个人每个项目的能力值,保证它们两两不同,求每次锦标
赛有多少人可能成为冠军。
$sol$
只要选手 $a$ 在某个项目上比选手 $b$ 强, $a$ 就可以淘汰 $b$,我们可以连
一条 $a$ 到 $b$ 的边。
对整个图求强连通分量,缩点后一定会形成一个竞赛图,拓扑序
最靠前的分量中的所有点都可能成为冠军。
每加入一个点时,我们可能需要合并拓扑序在一段区间内强连通
分量。用set按拓扑序维护每个强连通分量,对每个分量记录它的大
小,以及在每个项目上的最大和最小能力值,就可以直接在set上二分
找到需要合并的区间。
最多只会合并 $n - 1$ 次,时间复杂度 $O(nklogn)$
$code$
#include <bits/stdc++.h> using namespace std; #define gc getchar() inline int read() { int x = 0; char c = gc; while(c < '0' || c > '9') c = gc; while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc; return x; } #define Rep(i, a, b) for(int i = a; i <= b; i ++) #define MP(a, b) make_pair(a, b) const int N = 5e4 + 10; int A[N][15]; int Max[N][15]; int n, m; int fa[N], size[N], nxt[N], last = 1; int Answer[N]; set<pair<int, int> > S[15]; int Get(int x) { return fa[x] == x ? x : fa[x] = Get(fa[x]); } void Merge(int a, int b) { Rep(i, 1, m) Max[b][i] = max(Max[b][i], Max[a][i]); size[b] += size[a], fa[a] = b; } int main() { n = read(), m = read(); Rep(i, 1, n) Rep(j, 1, m) Max[i][j] = A[i][j] = read(); Rep(i, 1, n) fa[i] = i, size[i] = 1; Rep(i, 1, m) S[i].insert(MP(A[1][i], 1)); Answer[1] = 1; Rep(i, 2, n) { int a = 0, b = 0; set<pair<int, int> > :: iterator it; Rep(j, 1, m) { it = S[j].upper_bound(MP(A[i][j], i)); if(it != S[j].end()) { int t = Get((*it).second); if(!b || Max[t][j] < Max[b][j]) b = t; } if(it != S[j].begin()) { it --; int t = Get((*it).second); if(!a || Max[t][j] > Max[a][j]) a = t; } S[j].insert(MP(A[i][j], i)); } if(!a) nxt[i] = b; else if(!b) last = i, nxt[a] = i; else if(a == b) Merge(i, a); else if(Max[a][1] < Max[b][1]) nxt[a] = i, nxt[i] = b; else { for(int t = b; t != a; t = nxt[t], Merge(t, b)); Merge(i, b); nxt[b] = nxt[a]; if(a == last) last = b; } Answer[i] = size[last]; } Rep(i, 1, n) cout << Answer[i] << "\n"; return 0; }