• BZOJ 1093: [ZJOI2007]最大半连通子图


    1093: [ZJOI2007]最大半连通子图

    Time Limit: 30 Sec  Memory Limit: 162 MB

    Submit: 3662  Solved: 1449

    [Submit][Status][Discuss]

    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 ≤100000, 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

    题解

    两个强联通子图合并肯定是一个半联通子图。

    tarjan找出所有强联通子图并缩点。

    剩下DAG图按拓扑排序跑DP求出最大半联通子图即可。

    代码

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<iostream>
    #include<stack>
    #include<queue>
    #include<vector>
    using namespace std;
    const int N=100005,M=1000005;
    int n,m,x,k=1,nk=1,cnt,tot,ans1,ans2;
    int head[N],dfn[N],low[N],vis[N],belong[N],num[N],in[N],f[N],d[N];
    stack<int>st;
    queue<int>q;
    vector<int>g[N];
    struct edge{
    	int u,v,next;
    }e[M];
    void addedge(int u,int v){
    	e[k]=(edge){u,v,head[u]};
    	head[u]=k++;
    }
    void tarjan(int u){
    	dfn[u]=++cnt;
    	low[u]=cnt;
    	st.push(u);
    	vis[u]=1;
    	int v;
    	for(int i=head[u];i;i=e[i].next){
    		v=e[i].v;
    		if(!dfn[v]){
    			tarjan(v);
    			low[u]=min(low[u],low[v]);
    		}
    		else if(vis[v])low[u]=min(low[u],dfn[v]);
    	}
    	if(dfn[u]==low[u]){
    		tot++;
    		while(!st.empty()){
    			v=st.top();
    			st.pop();
    			vis[v]=0;
    			belong[v]=tot;
    			num[tot]++;
    			if(u==v)break;
    		}
    	}
    }
    void build(){
    	int v;
    	for(int i=1;i<=n;i++){
    		for(int j=head[i];j;j=e[j].next){
    			v=e[j].v;
    			if(belong[i]!=belong[v]){
    				g[belong[i]].push_back(belong[v]);
    				in[belong[v]]++;
    			}
    		}
    	}
    }
    void dp(){
    	for(int i=1;i<=tot;i++){
    		if(!in[i])q.push(i);
    		d[i]=num[i];
    		f[i]=1;
    	}
    	int u,v;
    	while(!q.empty()){
    		u=q.front();
    		q.pop();
    		for(int i=0;i<g[u].size();i++){
    			v=g[u][i];
    			in[v]--;
    			if(!in[v])q.push(v);
    			if(vis[v]==u)continue;
    			if(d[u]+num[v]>d[v]){
    				d[v]=d[u]+num[v];
    				f[v]=f[u];
    			}
    			else if(d[u]+num[v]==d[v])f[v]=(f[v]+f[u])%x;
    			vis[v]=u;
    		}
    	} 
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&x);
    	int u,v;
    	for(int i=1;i<=m;i++){
    		scanf("%d%d",&u,&v);
    		addedge(u,v);
    	}
    	for(int i=1;i<=n;i++){
    		if(!dfn[i])tarjan(i);
    	}
    	build();
    	dp();
    	for(int i=1;i<=tot;i++){
    		if(d[i]>ans1){
    			ans1=d[i];
    			ans2=f[i];
    		}
    		else if(d[i]==ans1)ans2=(ans2+f[i])%x;
    	}
    	printf("%d
    %d
    ",ans1,ans2);
    	return 0;
    }
  • 相关阅读:
    input 蓝边
    4.【ac自动机】模式串匹配
    3.【二叉树】最近公共祖先
    2.【动态规划】最长回文子串
    1. 【线段树】
    DbUtil
    oauth2
    cas
    Spring-security-web
    JSON Web Tokens
  • 原文地址:https://www.cnblogs.com/chezhongyang/p/7681832.html
Copyright © 2020-2023  润新知