自然地想到可以连边。只要
能够赢过
,就给
连一条有向边。
考虑到前
个人里面有
个人可能获胜,那么这
个人一定互相能够到达。
考虑维护这个强连通分量。
怎么更新?
新加入一个点。
第一种可能是这个点完爆前面所有点,那么前面的结果就可以
由此想到维护前面每一项的最大值。
第二种可能是这个点并不比前面某一个可能获胜的点有至少一项强,那么这个点暂时凉凉
第三种可能就是这个点比前面某一个可能获胜的点有至少一项强。
由此想到维护前面可能获胜的点每一项的最小值。
然而,还需要注意的是,如果这个点可能获胜,那么前面可能就会有一些点。
这些点本来不能获胜,然而刚好某几项比新加入的点强,那么这些点就可以获胜了。
难道要枚举前面每一个点判断??
更仔细地考虑。
前面没有被加入强连通分量的点,更具体地说是怎么样的:
这也就意味着它没有办法连向强连通分量。
那么它被完爆了。
发现最终图里就会出现许多强连通分量。不同的强连通分量之间是完爆和被完爆的关系。
我们可以把这些强连通分量缩点,组成一条链。
这个时候,新加入一个点,就要考虑它是和前面的强连通分量合并还是独立出来。
对于每个强连通分量我们维护它的每项最大和每项最小。
然后合并新点的时候,对于这个点找到完爆它的和被它完爆的(缩点之后的)点。
显然我们是有序地去维护多个强连通分量的。
那么每次我们把最大不被它完爆的和最小不完爆它的强连通分量跟这个点合并一下。
需要实现的操作比较简单,可以用
维护强连通分量之间的完爆链。
小心不要写炸
。
当然如果习惯手打一个平衡树那就手打吧,不过这道题算是不错的
模板题(??
#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;
}