天天看點

[模拟賽]世界杯

題意

有\(n(n\le 10^5)\)支球隊,每兩個球隊有\(k(k\le 5)\)個屬性。

當一個球隊的某一項屬性大于另一個球隊,則這個球隊有可能會戰勝另外的球隊。

每次會在剩餘未被淘汰的球隊裡随機選出兩個球隊進行比賽。

問有可能獲得冠軍的球隊有多少個?

分析

當兩個球隊互相可戰勝的時候,發現如果另一個球隊能戰勝其中一個隊伍,那麼這個球隊能戰勝這兩個隊伍。

我們給互相可戰勝的球隊連一條邊。

最終發現整張圖變成若幹個聯通塊,每個聯通快有明确的強弱關系。

這樣子最終答案就是最強的聯通塊的大小。

考慮加點。

每次增加一個點,我們就把這個點向與他戰勝關系不确定的聯通塊連邊,這樣子我們就可以把幾個聯通塊縮成一個塊。

暴力統計?

\(O(n^2k)\),不可接受。

平衡樹。

可以把所有的塊丢進set裡面。

反向定義小于号,隻要大于就傳回true,隻有明确小于才傳回false。

這樣子我們隻要正反比較兩次如果都為true就說明兩個塊的關系是不清楚的。

每次加點,我們用upper_bound查詢出第一個不是明确大于目前塊的聯通塊,如果這個聯通塊也大于目前塊,那麼這兩個塊的強弱關系不清楚,我們可以合并。

一直合并到下一個塊是明确小于目前塊的為止。

然後插入目前塊。

這樣子我們就能\(O(knlogn)\)處理問題了。

代碼

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <set>
#define file "s"
#define ll long long
using namespace std;
const int N=2009,inf=(1<<31)-1;
const int Maxn=N*(N-1)/2;
int read(){
	char c;int num,f=1;
	while(c=getchar(),!isdigit(c))if(c==\'-\')f=-1;num=c-\'0\';
	while(c=getchar(), isdigit(c))num=num*10+c-\'0\';
	return f*num;
}
int k;
struct Ub{
	int sz,mx[10],mn[10];
	Ub& operator +=(const Ub& rhs){
		sz+=rhs.sz;
		for(int i=1;i<=k;i++)
			mx[i]=max(mx[i],rhs.mx[i]),mn[i]=min(mn[i],rhs.mn[i]);
		return *this;
	}
};
bool operator <(const Ub a,const Ub b){
	for(int i=1;i<=k;i++)
		if(a.mx[i]>b.mn[i])
			return true;
	return false;
}
set <Ub> S;
int main()
{
	freopen(file".in","r",stdin);
	freopen(file".out","w",stdout);
	int n=read();k=read();
	while(n--){
		Ub qwq;
		qwq.sz=1;
		for(int i=1;i<=k;i++)
			qwq.mn[i]=qwq.mx[i]=read();
		set<Ub>::iterator it=S.upper_bound(qwq);
		while(1){
			if(it==S.end()|| !(*it<qwq) )break;
			qwq+=*it;
			S.erase(it++);
		}
		S.insert(qwq);
		printf("%d ",S.begin()->sz);
	}
	return 0;
}