Description
Mr. Chopsticks keeps N monsters, numbered from 1 to N. In order to train them, he holds N * (N - 1) / 2 competitions and asks the monsters to fight with each other. Any two monsters fight in exactly one competition, in which one of them beat the other. If monster A beats monster B, we say A is stronger than B. Note that the “stronger than” relation is not transitive. For example, it is possible that A beats B, B beats C but C beats A.
After finishing all the competitions, Mr. Chopsticks divides all the monsters into two teams T1 and T2, containing M and N – M monsters respectively, where each monster is in exactly one team. Mr. Chopsticks considers a team of monsters powerful if there is a way to arrange them in a queue (A1, A2, …, Am) such that monster Ai is stronger than monster Aj for any 1<=i < j<=m. Now Mr. Chopsticks wants to check whether T1 and T2 are both powerful, and if so, he wants to select k monsters from T2 to join T1 such that the selected monsters together with all the monsters in T1 can still form a powerful team and k is as large as possible. Could you help him?
Solution
终于知道为什么他们可以天天向我炫耀访问量了。。原来他们翻译了题目!!
这题奥妙重重,又是一道涨姿势题。。 竞赛图上get新技能,拓扑序。
那么首先我们对两个集合跑一遍拓扑排序,这样我们就可以得到 YES or NO 以及两个集合中元素的拓扑序
判断YES或NO的时候拓扑排序或者暴力 O(N2) 判断都可以.
接下来对于 T2 中的每个点,只需要在 T1 中扫一遍就可以判断出是否可以放进 T1 ,如果不能放进去就直接丢掉,如果可以就确定放在哪个位置.
注意因为是个竞赛图,所以这个位置是唯一的.
给 T2 剩下的点按在 T2 中的拓扑顺序(因为是竞赛图,所以这个顺序也是唯一的)标上它们在 T1 中的位置值,对得到的这个位置值数列求最长上升子序列的长度就是答案.
总复杂度 O(N2) .
来论述一下正确性
首先因为是竞赛图,两个子图也是竞赛图。
在没有还的情况下有且只有一个入度为0的点。
删掉一个点依旧是竞赛图。。
和上面那幅图神似。。这就可以说明为什么拓扑序是一定的
然后说说为什么答案是LIS
因为拓扑序在后的一定被更多人指向
并且是无环竞赛图,那么在后面的一定被在前面的指到。
诶!
为什么是LIS出来了,在保证在加入
T1
后无环,
T2
的点在
T1
中也应当是一个上升序列,所以答案是LIS
懒得一逼。。LIS都 O(n2) 写。。
Code
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<time.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
typedef double db;
typedef unsigned ud;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef vector<int> vec;
typedef vector<pii> vecp;
#define fi first
#define se second
#define pb push_back
#define ph push
const int INF=(ud)-1>>1;
const ll inf=(ull)-1ll>>1;
template<class T>void rd(T &a){
a=0;char c;
while(c=getchar(),!isdigit(c));
do a=a*10+(c^48);
while(c=getchar(),isdigit(c));
}
template<class T>void nt(T x){
if(!x)return;
nt(x/10);
putchar(48+x%10);
}
template<class T>void pt(T x){
if(!x)putchar('0');
else nt(x);
}
template<class T>void Max(T &a,T b){
if(a<b)a=b;
}
template<class T>void Min(T &a,T b){
if(a==-1||a>b)a=b;
}
const int M=1e3+5;
bool mp[M][M];
inline void rdb(bool &b){
static char c;
for(;c=getchar(),!isdigit(c););
b=c-'0';
}
struct Edge{
int to,nxt;
}G[M*M];
int head[M],tot_edge;
inline void add_edge(int from,int to){
G[tot_edge]=(Edge){to,head[from]};
head[from]=tot_edge++;
}
int d[M],tid[M],n,k,w,que[M],A[M],B[M],C[M],dp[M];
bool mark[M];
inline bool Topology(bool f){
int L=0,R=0;
if(f)for(int i=1;i<=k;++i){
if(!d[A[i]]){
que[R++]=A[i];break;
}
}
else for(int i=1;i<=w;++i){
if(!d[B[i]]){
que[R++]=B[i];break;
}
}
for(;L<R;){
int v=que[L++];
tid[v]=L;
for(int i=head[v];~i;i=G[i].nxt){
int to=G[i].to;
if(mark[to]^mark[v])continue;
if((--d[to])==0)que[R++]=to;
}
}
if(!f)return R==w;
return R==k;
}
char str[M<<1];
inline void gao(){
//Init
memset(head,-1,sizeof(head));
memset(d,0,sizeof(d));
memset(tid,0,sizeof(tid));
memset(mark,0,sizeof(mark));
memset(dp,0,sizeof(dp));
memset(C,0,sizeof(C));
tot_edge=w=0;
for(int i=1;i<=n;++i){
gets(str);
for(int j=1;j<=n;++j)
mp[i][j]=str[j-1<<1]-'0';
}
for(int i=1;i<=k;++i)
rd(A[i]),mark[A[i]]=1;
for(int i=1;i<=n;++i)
if(!mark[i])B[++w]=i;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(mp[i][j]&&(mark[i]==mark[j]))
add_edge(i,j),++d[j];
bool flag=Topology(0);
flag&=Topology(1);
printf(flag?"YES ":"NO\n");
if(!flag)return;
w=0;
for(int i=1;i<=n;++i){//tid表示拓扑序
if(mark[i])continue;
int Mx=0,Mn=INF;
for(int j=1;j<=k;++j)
if(mp[A[j]][i])
Max(Mx,tid[A[j]]);
for(int j=1;j<=k;++j)
if(mp[i][A[j]])
Min(Mn,tid[A[j]]);
if(Mn<Mx)mark[i]=1;
}
for(int i=1;i<=n;++i)
if(!mark[i])B[++w]=i;
for(int i=1;i<=w;++i)
d[B[i]]=0;
for(int i=1;i<=w;++i)
for(int j=1;j<=w;++j)
if(mp[B[i]][B[j]])++d[B[j]];
Topology(0);
for(int i=1;i<=w;++i){
int Mx=0;
for(int j=1;j<=k;++j)
if(mp[A[j]][B[i]])Max(Mx,tid[A[j]]);
C[tid[B[i]]]=Mx+1;
}
int ans=0;
for(int i=1;i<=w;++i){
int Mx=0;
for(int j=1;j<i;++j)
if(C[j]<=C[i])Max(Mx,dp[j]);
dp[i]=Mx+1;
Max(ans,dp[i]);
}
cout<<ans<<endl;
}
//#define LOCAL
int main(){
#ifdef LOCAL
freopen("1003.in","r",stdin);
freopen("ans.out","w",stdout);
#endif
for(;scanf("%d %d\n",&n,&k),n;)gao();
return 0;
}