题意
Berland要举行n次锦标赛,第一次只有一个人,之后每一次会新
加入一个人。锦标赛中有k种运动项目,每个人在这k种项目上都有一
个能力值,每次会选择任意两个还未被淘汰的人进行某个项目的比
赛,能力值高的人胜出,输的人被淘汰,直至只剩下一个人成为冠
军。给出每个人每个项目的能力值,保证它们两两不同,求每次锦标赛有多少人可能成为冠军。
题解
并不会做这题。。
一开始想的是,除非一个人被另外一个人碾压,否则一定行
很快就发现,这样是错的。。
然后就想到,如果iii可以赢jjj,那么就iii向jjj连一条边
那么一个点可以拿冠军,当且仅当,他可以到达所有点。。
于是,我们可以强连通缩点一下,得到一个DAG。。
然后对于一个普通的DAG剩下似乎就不可做了。。
膜了题解
发现有一个很重要的性质没有想起来
那就是这是一个竞赛图
想不起来的主要原因是确实没做过竞赛图的题目。。
竞赛图显然有一个重要的性质
就是你缩完点之后只有一个入度为000的点
那么答案就是这个强连通分量的大小!
这样就省事很多了
每一次加入一个点,我们可以把一些联通块连起来
怎么连呢
我们知道,如果x能打败y,y也可以打败x
那么就可以看做一个点
我们仔细思考一个这个条件是什么意思
如果a>=ba>=ba>=b且b>=ab>=ab>=a,那么a=ba=ba=b
显然,在这里等号具有传递性!!!
那么我们其实加入一个点,就会使得一些原来不相等的点相等
我们用set定义小于号
就可以得到和x相等的点的编号了,连在一起即可
感觉这题无论是竞赛题的性质还是用set来维护都十分地巧妙
CODE:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
int n,k;
struct qq
{
int mx[11],mn[11];int size;
};
bool operator < (qq x,qq y)
{
for (int u=1;u<=k;u++)if (x.mx[u]>y.mn[u])return false;return true;
}
set<qq> s;
int read ()
{
char ch=getchar();int x=0;while (ch<'0'||ch>'9') ch=getchar();while (ch>='0'&&ch<='9') {
x=x*10+ch-'0';ch=getchar();}return x;
}
int main()
{
n=read();k=read();for (int u=1;u<=n;u++){
qq x;for (int i=1;i<=k;i++){
x.mn[i]=x.mx[i]=read();x.size=1;}set<qq> :: iterator l,r;l=s.lower_bound(x);r=s.upper_bound(x);while (l!=r){
x.size=x.size+(*l).size;for (int u=1;u<=k;u++){
x.mn[u]=min(x.mn[u],(*l).mn[u]);x.mx[u]=max(x.mx[u],(*l).mx[u]);}s.erase(l++);//l++;}s.insert(x);printf("%d\n",(*s.rbegin()).size);}return 0;
}