BZOJ 1093: [ZJOI2007]最大半连通子图
2016-07-10 09:48
387 查看
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的余数。
Solution
刚看到这题确实有点晕。半联通的图都有什么性质,我咋知道==
然后其实把样例画出来,大概就可以知道了。
对于那些强连通的分量,尽情的缩点。
然后那到的图,我们只要找出一条最长链即可。
这个用拓扑排序就可以搞。
然后关于方案数,可以在拓扑排序的时候dp,非常简单。
尼玛第一次交竟然PE了==那个涕泪流的啊。。
一直觉得拓扑排序没什么用,然而当有用的时候真的非常神啊==
Code
一个有向图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的余数。
Solution
刚看到这题确实有点晕。半联通的图都有什么性质,我咋知道==
然后其实把样例画出来,大概就可以知道了。
对于那些强连通的分量,尽情的缩点。
然后那到的图,我们只要找出一条最长链即可。
这个用拓扑排序就可以搞。
然后关于方案数,可以在拓扑排序的时候dp,非常简单。
尼玛第一次交竟然PE了==那个涕泪流的啊。。
一直觉得拓扑排序没什么用,然而当有用的时候真的非常神啊==
Code
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #include<vector> #include<stack> #include<queue> using namespace std; const int M=1e5+5; int n,m,P; inline void rd(int &a){ a=0;char c; while(c=getchar(),!isdigit(c)); do a=a*10+(c^48); while(c=getchar(),isdigit(c)); } #define vec vector<int> #define pb push_back #define ph push vec edge[M],G[M],scc[M]; stack<int>stk; int sccid[M],pre[M],lowlink[M],allc,dfs_clock,sz[M],d[M]; inline void Min(int &a,int b){if(a>b)a=b;} inline void Max(int &a,int b){if(a<b)a=b;} void predfs(int v){ pre[v]=lowlink[v]=++dfs_clock; stk.push(v); for(int i=0;i<edge[v].size();++i){ int to=edge[v][i]; if(!pre[to])predfs(to),Min(lowlink[v],lowlink[to]); else if(!sccid[to])Min(lowlink[v],pre[to]); } if(pre[v]==lowlink[v]){ sccid[v]=++allc,sz[allc]=1; for(;!stk.empty();){ int to=stk.top();stk.pop(); scc[allc].pb(to); if(to==v)break; ++sz[sccid[to]=allc]; } } } int mark[M]; inline void Graph(){ for(int i=1;i<=allc;++i){ for(int j=0;j<scc[i].size();++j){ int v=scc[i][j]; for(int k=0;k<edge[v].size();++k){ int to=sccid[edge[v][k]]; if(i!=to&&mark[to]!=i) G[i].pb(to),++d[to],mark[to]=i; } } } } queue<int>que; int len[M],dp[M]; inline void Mod_add(int &a,int b){if((a+=b)>=P)a-=P;} void Topology(){ for(int i=1;i<=allc;++i) if(d[i]==0)que.ph(i),len[i]=sz[i],dp[i]=1; for(;!que.empty();){ int v=que.front();que.pop(); for(int i=0;i<G[v].size();++i){ int to=G[v][i]; if(!(--d[to]))que.ph(to); if(len[to]==len[v]+sz[to])Mod_add(dp[to],dp[v]); if(len[to]<len[v]+sz[to])dp[to]=dp[v],len[to]=len[v]+sz[to]; } } } int main(){ cin>>n>>m>>P; for(int i=1,a,b;i<=m;++i) rd(a),rd(b),edge[a].pb(b); for(int i=1;i<=n;++i) if(!pre[i])predfs(i); Graph(); Topology(); int ans=0,res=0; for(int i=1;i<=allc;++i) Max(ans,len[i]); for(int i=1;i<=allc;++i) if(len[i]==ans)Mod_add(res,dp[i]); cout<<ans<<endl<<res<<endl; return 0; }
相关文章推荐
- 详解Android应用中屏幕尺寸的获取及dp和px值的转换
- 基于Android中dp和px之间进行转换的实现代码
- Android中dip、dp、sp、pt和px的区别详解
- 详解图的应用(最小生成树、拓扑排序、关键路径、最短路径)
- LFC1.0.0 版本发布
- Android dpi,dip,dp的概念以及屏幕适配
- Android px、dp、sp之间相互转换
- HP data protector软件学习1--基本角色与基本工作流程
- HP data protector软件学习2--软件组成与界面介绍
- android中像素单位dp、px、pt、sp的比较
- Android对px和dip进行尺寸转换的方法
- 初学图论-Kahn拓扑排序算法(Kahn's Topological Sort Algorithm)
- Android根据分辨率进行单位转换-(dp,sp转像素px)
- android 尺寸 dp,sp,px,dip,pt详解
- DP问题各种模型的状态转移方程
- POJ-1695-Magazine Delivery-dp
- nyoj-1216-整理图书-dp
- TYVJ1193 括号序列解题报告
- 对DP的一点感想
- TYVJ上一些DP的解题报告