【刷题】BZOJ 1093 [ZJOI2007]最大半连通子图

Description

  一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意
两点u,v,存在一条u到v的有向路径或者从v到u的有向路径。若G'=(V',E')满足V'?V,E'是E中所有跟V'有关的边,
则称G'是G的一个导出子图。若G'是G的导出子图,且G'半连通,则称G'为G的半连通子图。若G'是G所有半连通子图
中包含节点数最多的,则称G'是G的最大半连通子图。给定一个有向图G,请求出G的最大半连通子图拥有的节点数K
,以及不同的最大半连通子图的数目C。由于C可能比较大,仅要求输出C对X的余数。

Input

  第一行包含两个整数N,M,X。N,M分别表示图G的点数与边数,X的意义如上文所述接下来M行,每行两个正整
数a, b,表示一条有向边(a, b)。图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次。N ≤1
00000, M ≤1000000;对于100%的数据, X ≤10^8

Output

  应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.

Sample Input

6 6 20070603
1 2
2 1
1 3
2 4
5 6
6 4

Sample Output

3
3

Solution

考虑如果我们把图缩强连通分量后,原图中的一个半联通分量就是缩点后的DAG的一条链
所以题目要求的就是DAG上的最长链的长度及方案数
这个的话拓扑dp一下就好了

#include<bits/stdc++.h>
#define ui unsigned int
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
const int MAXN=100000+10,MAXM=1000000+10;
int n,m,Mod,e,beg[MAXN],nex[MAXM],to[MAXM],Be[MAXN],cnt,DFN[MAXN],LOW[MAXN],Visit_Num,Stack[MAXN],Stack_Num,In_Stack[MAXN],sum[MAXN],f[MAXN],ans1,in[MAXN];
ll g[MAXN],ans2;
struct node{
	int u,v;
	inline bool operator < (const node &A) const {
		return u<A.u||(u==A.u&&v<A.v);
	};
	inline bool operator == (const node &A) const {
		return u==A.u&&v==A.v;
	};
};
node side[MAXM];
std::queue<int> q;
template<typename T> inline void read(T &x)
{
	T data=0,w=1;
	char ch=0;
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
	x=data*w;
}
template<typename T> inline void write(T x,char ch='\0')
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
	if(ch!='\0')putchar(ch);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline void insert(int x,int y)
{
	to[++e]=y;
	nex[e]=beg[x];
	beg[x]=e;
}
inline void Tarjan(int x)
{
	DFN[x]=LOW[x]=++Visit_Num;
	Stack[++Stack_Num]=x;
	In_Stack[x]=1;
	for(register int i=beg[x];i;i=nex[i])
		if(!DFN[to[i]])Tarjan(to[i]),chkmin(LOW[x],LOW[to[i]]);
		else if(In_Stack[to[i]]&&DFN[to[i]]<LOW[x])LOW[x]=DFN[to[i]];
	if(DFN[x]==LOW[x])
	{
		int temp;++cnt;
		do{
			temp=Stack[Stack_Num--];
			In_Stack[temp]=0;
			Be[temp]=cnt;
			sum[cnt]++;
		}while(temp!=x);
	}
}
inline void toposort()
{
	for(register int i=1;i<=cnt;++i)
		if(!in[i])q.push(i),f[i]=sum[i],g[i]=1;
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		for(register int i=beg[x];i;i=nex[i])
		{
			if(f[x]+sum[to[i]]==f[to[i]])(g[to[i]]+=g[x])%=Mod;
			else if(f[x]+sum[to[i]]>f[to[i]])f[to[i]]=f[x]+sum[to[i]],g[to[i]]=g[x];
			in[to[i]]--;
			if(!in[to[i]])q.push(to[i]);
		}
	}
}
int main()
{
	read(n);read(m);read(Mod);
	for(register int i=1;i<=m;++i)
	{
		int u,v;read(u);read(v);
		insert(u,v);
		side[i]=(node){u,v};
	}
	for(register int i=1;i<=n;++i)
		if(!DFN[i])Tarjan(i);
	e=0;memset(beg,0,sizeof(beg));
	for(register int i=1;i<=m;++i)side[i].u=Be[side[i].u],side[i].v=Be[side[i].v];
	std::sort(side+1,side+m+1);
	m=std::unique(side+1,side+m+1)-side-1;
	for(register int i=1;i<=m;++i)
		if(side[i].u!=side[i].v)insert(side[i].u,side[i].v),in[side[i].v]++;
	toposort();
	for(register int i=1;i<=cnt;++i)chkmax(ans1,f[i]);
	for(register int i=1;i<=cnt;++i)
		if(f[i]==ans1)(ans2+=g[i])%=Mod;
	printf("%d\n%lld\n",ans1,ans2);
	return 0;
}
posted @ 2018-08-22 11:36  HYJ_cnyali  阅读(...)  评论(...编辑  收藏