[CF878C] Tournament [平衡樹維護強連通分量][set]
阿新 • • 發佈:2018-11-12
自然地想到可以連邊。只要
能夠贏過
,就給
連一條有向邊。
考慮到前
個人裡面有
個人可能獲勝,那麼這
個人一定互相能夠到達。
考慮維護這個強連通分量。
怎麼更新?
新加入一個點。
第一種可能是這個點完爆前面所有點,那麼前面的結果就可以
由此想到維護前面每一項的最大值。
第二種可能是這個點並不比前面某一個可能獲勝的點有至少一項強,那麼這個點暫時涼涼
第三種可能就是這個點比前面某一個可能獲勝的點有至少一項強。
由此想到維護前面可能獲勝的點每一項的最小值。
然而,還需要注意的是,如果這個點可能獲勝,那麼前面可能就會有一些點。
這些點本來不能獲勝,然而剛好某幾項比新加入的點強,那麼這些點就可以獲勝了。
難道要列舉前面每一個點判斷??
更仔細地考慮。
前面沒有被加入強連通分量的點,更具體地說是怎麼樣的:
這也就意味著它沒有辦法連向強連通分量。
那麼它被完爆了。
發現最終圖裡就會出現許多強連通分量。不同的強連通分量之間是完爆和被完爆的關係。
我們可以把這些強連通分量縮點,組成一條鏈。
這個時候,新加入一個點,就要考慮它是和前面的強連通分量合併還是獨立出來。
對於每個強連通分量我們維護它的每項最大和每項最小。
然後合併新點的時候,對於這個點找到完爆它的和被它完爆的(縮點之後的)點。
顯然我們是有序地去維護多個強連通分量的。
那麼每次我們把最大不被它完爆的和最小不完爆它的強連通分量跟這個點合併一下。
需要實現的操作比較簡單,可以用
維護強連通分量之間的完爆鏈。
小心不要寫炸
。
當然如果習慣手打一個平衡樹那就手打吧,不過這道題算是不錯的
模板題(??
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<set>
#include<ctime>
using namespace std;
int n, k;
struct elem {
int u[15];
int d[15];
int siz;
void init (const int &a) {
for (int i = 1; i <= k; ++i) {
u[i] = d[i] = a;
}
siz = 0;
}
bool operator < (const elem &b) const {
for (int i = 1; i <= k; ++i) {
if (u[i] > b.d[i]) return 0;
}
return 1;
}
void merge (const elem &b) {
siz += b.siz;
for (int i = 1; i <= k; ++i) {
u[i] = max(u[i], b.u[i]);
d[i] = min(d[i], b.d[i]);
}
}
};
int qtot;
set<elem>::iterator Q[50005];
set<elem>S;
int main() {
scanf("%d%d", &n, &k);
elem tmp;
for (int i = 1; i <= n; ++i) {
tmp.siz = 1;
for (int j = 1; j <= k; ++j) {
scanf("%d", &tmp.d[j]);
tmp.u[j] = tmp.d[j];
}
set<elem>::iterator pre = S.lower_bound(tmp); // *don't use lower_bound(S.begin(), S.end(), tmp)
set<elem>::iterator nxt = S.upper_bound(tmp); // *
set<elem>::iterator it;
qtot = 0;
for (it = pre; it != nxt; ++it) {
Q[++qtot] = it;
}
for (int j = 1; j <= qtot; ++j) {
tmp.merge(*Q[j]);
S.erase(Q[j]);
}
S.insert(tmp);
printf("%d\n", S.rbegin() -> siz); //fix
}
return 0;
}