【BZOJ1093】【ZJOI2007】最大半联通子图 [DP][Tarjan]
最大半连通子图
Time Limit: 30 Sec Memory Limit: 162 MB
[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)不会出现两次。
Output
应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.
Sample Input
1 2
2 1
1 3
2 4
5 6
6 4
Sample Output
3
HINT
Main idea
求最大半联通子图大小与个数。(最大半联通子图定义:在这个图内对于任意节点u,v,存在一条u->v的路径)
Solution
先跑一遍Tarjan,得到了两两连通的图,然后考虑如何加入单向连通的点集,显然两个强连通分量之间要是有连边的话,就可以满足这两个强连通分量的点单向连通,符合题意。
那么答案显然就是在缩点后的DAG(有向无环图)上的最长路径。
用拓扑+DP(本质是在拓扑序上的DP)可以求出即为Ans,然后在跑的时候用一个数组f[i]统计一下相同的个数,注意更新dist的时候也要更新f,最后如果dist[i]=Ans,那么累加f[i],即为答案。
Code
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std; const int ONE=; int n,m,MOD;
int x,y;
int Next[ONE],First[ONE],Go[ONE],tot;
int next[ONE],first[ONE],go[ONE],Input[ONE];
int dist[ONE];
int T,t;
int tou,wei,jishu;
int q[ONE];
int Ans,num,f[ONE];
int Dfn[ONE],Low[ONE],vis[ONE],F[ONE],Num[ONE]; struct power
{
int u,v;
}a[ONE]; int cmp(const power &a,const power &b)
{
if(a.u==b.u) return a.v<b.v;
return a.u<b.u;
} int rule(const power &a,const power &b)
{
return (a.u==b.u && a.v==b.v);
} int get()
{
int res,Q=; char c;
while( (c=getchar())< || c>)
if(c=='-')Q=-;
if(Q) res=c-;
while((c=getchar())>= && c<=)
res=res*+c-;
return res*Q;
} int Add(int u,int v)
{
Next[++tot]=First[u]; First[u]=tot; Go[tot]=v;
} int Add_edge(int u,int v)
{
next[++tot]=first[u]; first[u]=tot; go[tot]=v; Input[v]++;
} void Tarjan(int u)
{
Dfn[u]=Low[u]=++T;
vis[u]=;
q[++t]=u;
int v;
for(int e=First[u];e;e=Next[e])
{
int v=Go[e];
if(!Dfn[v])
{
Tarjan(v);
Low[u]=min(Low[u],Low[v]);
}
else if(vis[v])
Low[u]=min(Low[u],Dfn[v]);
} if(Low[u]==Dfn[u])
{
jishu++;
do
{
v=q[t--];
F[v]=jishu;
vis[v]=;
Num[jishu]=Num[jishu]+;
}while(v!=u);
}
} void Rebuild()
{
num=;
for(int u=;u<=n;u++)
{
for(int e=First[u];e;e=Next[e])
{
int v=Go[e];
if(F[u]!=F[v])
{
a[++num].u=F[u];
a[num].v=F[v];
}
}
} sort(a+,a+num+,cmp);
num=unique(a+,a+num+,rule)--a; for(int i=;i<=num;i++)
{
Add_edge(a[i].u,a[i].v);
}
} void Topufirst()
{
for(int v=;v<=jishu;v++)
{
if(!Input[v]) q[++wei]=v;
dist[v]=Num[v];
f[v]=;
Ans=max(Ans,dist[v]);
}
} void TopuA()
{
while(tou<wei)
{
int u=q[++tou];
for(int e=first[u];e;e=next[e])
{
int v=go[e];
if(dist[v]<dist[u]+Num[v])
{
dist[v]=dist[u]+Num[v];
f[v]=f[u];
Ans=max(Ans,dist[v]);
}
else
if(dist[v]==dist[u]+Num[v]) f[v]=(f[v]+f[u])%MOD;
if(!(--Input[v])) q[++wei]=v;
}
}
} int main()
{
n=get(); m=get(); MOD=get();
for(int i=;i<=m;i++)
{
x=get(); y=get();
Add(x,y);
} for(int i=;i<=n;i++)
if(!Dfn[i]) Tarjan(i); tot=;
Rebuild(); tou=; wei=;
Topufirst(); TopuA(); tot=;
for(int i=;i<=jishu;i++)
if(dist[i]==Ans) tot=(tot+f[i])%MOD; printf("%d\n%d",Ans,tot);
}
【BZOJ1093】【ZJOI2007】最大半联通子图 [DP][Tarjan]的更多相关文章
- 【BZOJ1093】[ZJOI2007]最大半联通子图(Tarjan,动态规划)
[BZOJ1093][ZJOI2007]最大半联通子图(Tarjan,动态规划) 题面 BZOJ 洛谷 洛谷的讨论里面有一个好看得多的题面 题解 显然强连通分量对于题目是没有任何影响的,直接缩点就好了 ...
- bzoj1093 [ZJOI2007]最大半联通子图 缩点 + 拓扑序
最大半联通子图对应缩点后的$DAG$上的最长链 复杂度$O(n + m)$ #include <cstdio> #include <cstring> #include < ...
- bzoj1093[ZJOI2007]最大半连通子图(tarjan+拓扑排序+dp)
Description 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u ...
- BZOJ1093 [ZJOI2007]最大半连通子图 【tarjan缩点 + DAG最长路计数】
题目 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意 两点u,v,存在一条u到v的有向路径或者从v到u的有向路径.若G ...
- [ZJOI2007]最大半连通子图(Tarjan,拓扑序DP)
[ZJOI2007]最大半连通子图 题目描述 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v ...
- [bzoj 1093][ZJOI2007]最大半联通子图(强联通缩点+DP)
题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1093 分析: 首先肯定是先把强联通全部缩成一个点,然后成了一个DAG 下面要知道一点: ...
- BZOJ1093: [ZJOI2007]最大半连通子图(tarjan dp)
题意 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径.若G' ...
- 【tarjan 拓扑排序 dp】bzoj1093: [ZJOI2007]最大半连通子图
思维难度不大,关键考代码实现能力.一些细节还是很妙的. Description 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于 ...
- bzoj1093: [ZJOI2007]最大半连通子图 scc缩点+dag上dp
一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径.若G'=(V ...
随机推荐
- Java 变量和输入输出
一些重要知识 一个源文件里只能有一个public类,其它类数量不限.文件名与public类名相同 JAVA程序严格区分大小写 JAVA应用程序的执行入口是main方法固定写法:public stati ...
- P4编程环境搭建遇到的问题与解决方法
在经历了无数的折腾之后,算是折腾,最后采用的是陈翔学长的脚本加上可爱的shell调整装好的. 链接:p4Install 也许是ubuntu18.04的问题,也有可能是我自己把这个系统折腾的有点杂乱的原 ...
- 修改IntelliJ IDEA字体
- node中的path.resolve
path.resolve([arg1,arg2,...])根据参数的不同,返回值存在两种情况. 以下为参数的两种情况: 1.每个参数都不带'/',比如path.resolve(),或者path.res ...
- 第三部分shell编程3(shell脚本2)
7. if 判断一些特殊用法 if [ -z $a ] 这个表示当变量a的值为空时会怎么样if grep -q '123' 1.txt; then 表示如果1.txt中含有'123'的行时会怎么样if ...
- Mysql 基本语句练习
一.怎样查看数据库信息? desc 数据库名; 二.怎样查看数据表信息? desc 表名: //查看表的属性和属性值 或者用select语句: //查看表的行记录信息 select ...
- kaptcha验证码在windows下正常,在linux下无法显示
有几种情况,记录备忘: 1.两个环境字体不一样,linux环境下可能没有字体,重新安装字体即可. 2.tomcat等容器下没有temp目录,手动建立即可. 3.如果报找不到类的错误,检查JDK是否正确 ...
- 【题解】APIO2018 Duathlon 铁人两项
首先对于给出的图建立圆方树,然后我们分类讨论每一个点作为中间的中转站出现的情况有多少种,累积到 \(ans\) 中. 对于圆点:在任意两个子树内分别选出一个节点都是合法的. 对于方点:连接向方点的点均 ...
- BZOJ1026:[SCOI2009]windy数——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=1026 Description windy定义了一种windy数.不含前导零且相邻两个数字之差至少为2 ...
- BZOJ2705:[SDOI2012]Longge的问题——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=2705 Description Longge的数学成绩非常好,并且他非常乐于挑战高难度的数学问题.现在 ...