题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3795
http://vjudge.net/contest/view.action?cid=52151#problem/H
浙大月赛题
1.题意:
有N个人,M条边,边(u,v)表示u的年龄不小于v,若把这N个人分为很多组,要求每一组中的年龄无法相互比较,求组数最小值。
2.题解:
(1)掌握强联通、偏序集(链和反链)的基础知识。
(2)设(X,<=)是一个有限偏序集。反链是X的一个子集A,它的任意两个元素都不可比。相比之下,链是X的一个子集C,他的每个元素都可比。本题就是求反链数的最小值。
(3)定理:设(X,<=)是一个有限偏序集,而设r是的链的最大大小。则X可以被划分成r个反链,但不能划分成少于r个反链。
证明:假如已经找到了p个反链,自然就可以找到>=p个反链,只要把当前其中任意一个大于2的反链拆成2个反链,就可行。所以只要证明X可以分为r个反链即可。令X1=X,并设A1是X的极小元的集合。从X1中删除A1个元素的到X2,对于X2中的每一个元素,存在A1的某个元素在这个偏序之下在这个元素下方(同一条链上的元素有偏序关系,每个非极小元都可以找到对应的极小元)。所以这样分下去可以得到A1|A2|A3|...|Ap,对于Aj中的每个元素,总可以从Aj-1中找到某个偏序关系在它下方的元素(2<=j<=p)。这样就得到一个链a1<a2<a3<...<ap。由于r是链的最大长度,所以p<=r,又因为x可以被划分为p个反链,所以有r<=p。因此r=p定理得证。(这句话要仔细想想)
(4)所以本题就是找一个最长链,用长度r就是答案。
(5)但是题目可以有环,因为偏序关系是“不小于”。所以一开始用强联通缩点,缩点后每个强连通分量的点权就是这个强联通分量的点数。
(6)强联通分量用Tarjan,找最长链就是一个DFS(DP)。
code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
using namespace std;
const int MAXN=111111,MAXM=333333;
struct Edge{
int from,to,next;
Edge(){}
Edge(int f,int t,int n):from(f),to(t),next(n){}
};
struct SCC{
Edge edge[MAXM];
int head[MAXN],tot,n,pre[MAXN],lowlink[MAXN],sccno[MAXN],dfs_clock,scc_cnt;
stack<int>S;
void init(){
memset(head,-1,sizeof head);
tot=0;
}
void add(int f,int t){
edge[tot]=Edge(f,t,head[f]);
head[f]=tot++;
}
void dfs(int u){
pre[u]=lowlink[u]=++dfs_clock;
S.push(u);
for(int p=head[u];~p;p=edge[p].next){
int v=edge[p].to;
if(!pre[v]){
dfs(v);
lowlink[u]=min(lowlink[u],lowlink[v]);
}else if(!sccno[v]){
lowlink[u]=min(lowlink[u],pre[v]);
}
}
if(lowlink[u]==pre[u]){
scc_cnt++;
for(;;){
int x=S.top();S.pop();
sccno[x]=scc_cnt;
if(x==u)break;
}
}
}
void find_scc(int _n){
n=_n;
dfs_clock=scc_cnt=0;
memset(sccno,0,sizeof sccno);
memset(pre,0,sizeof pre);
while(!S.empty()) S.pop();
for(int i=1;i<=n;i++)
if(!pre[i])dfs(i);
}
}ga;
struct Graph{
Edge edge[MAXM];
int head[MAXN],tot,dp[MAXN],w[MAXN];
bool vis[MAXN];
void init(){
memset(head,-1,sizeof head);
memset(dp,0,sizeof dp);
memset(vis,0,sizeof vis);
memset(w,0,sizeof w);
tot=0;
}
void add(int f,int t){
edge[tot]=Edge(f,t,head[f]);
head[f]=tot++;
}
int dfs(int u){
if(vis[u]) return dp[u];
vis[u]=1;
int ret=0;
for(int p=head[u];~p;p=edge[p].next){
int v=edge[p].to;
ret=max(ret,dfs(v));
}
return dp[u]=ret+w[u];
}
}gb;
int main()
{
// freopen("data.in","r",stdin);
int N,M,u,v;
while(scanf("%d%d",&N,&M)==2){
ga.init();
for(int i=0;i<M;i++){
scanf("%d%d",&u,&v);
ga.add(u,v);
}
ga.find_scc(N);
gb.init();
for(int u=1;u<=N;u++){
for(int p=ga.head[u];~p;p=ga.edge[p].next){
int x=ga.sccno[u];
int y=ga.sccno[ga.edge[p].to];
if(x!=y)
gb.add(x,y);
}
}
for(int u=1;u<=N;u++){
int x=ga.sccno[u];
gb.w[x]++;
}
int ans=0;
for(int u=1;u<=ga.scc_cnt;u++)
ans=max(ans,gb.dfs(u));
printf("%d\n",ans);
}
return 0;
}