P2272 [ZJOI2007]最大半连通子图

题意

题目描述

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

输入格式

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

输出格式

应包含两行,第一行包含一个整数\(K\)。第二行包含整数\(C\mod X\)。

输入输出样例

输入样例#1:

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

输出样例#1:

3
3

说明

对于\(100\%\)的数据,\(N\le 100000,M\le 1000000,X\le 10^8\)。

思路

先来想两个问题:

  1. 该图是强连通图,那么答案是多少?
  2. 该图是有向无环图,那么答案是多少?

对于第一个问题,任意两点互相可达,问题变得很简单,答案就是原图;对于第二个问题,我们可以直接\(DAG\ DP\)完美解决。

那么对于任意的一张图,我们用\(Tarjan\)缩点之后,答案不久呼之欲出了吗?所以这题只需要在缩点之后的图上\(DP\)就好了。

不过还有个细节:因为要求方案数,所以不能建重边。比如点\(1\)向点\(2\)建了两条边。从\(1\)点向周围拓展时,第一次扫描到\(2\),我们更新了\(2\)的答案;第二次扫描到\(2\),我们又更新了\(2\)的答案。这显然是不被允许的。所以开个\(set\)判断一下重边就好啦。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PLL;
const LL MAXN=1e5+5,MAXM=1e6+5;
LL n,m,p,ans1,ans2,tot,dfn[MAXN],low[MAXN];
LL cnt,top[MAXN],to[MAXM],nex[MAXM];
LL _cnt,_top[MAXN],_to[MAXM],_nex[MAXM];
LL js,bel[MAXN],sz[MAXN],deg[MAXN],val[MAXN],tms[MAXN];
bool vis[MAXN];
stack<LL>S;
set<PLL>SS;
LL read()
{
LL re=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
return re;
}
void tarjan(LL now)
{
dfn[now]=low[now]=++tot,vis[now]=true,S.push(now);
for(LL i=top[now];i;i=nex[i])
if(!dfn[to[i]]) tarjan(to[i]),low[now]=min(low[now],low[to[i]]);
else if(vis[to[i]]) low[now]=min(low[now],dfn[to[i]]);
if(dfn[now]==low[now])
{
bel[now]=++js,vis[now]=false,sz[js]=1;
while(S.top()!=now) bel[S.top()]=js,vis[S.top()]=false,sz[js]++,S.pop();
S.pop();
}
}
void work()
{
memset(vis,false,sizeof vis);
queue<LL>Q;
for(LL i=1;i<=js;i++) if(!deg[i]) val[i]=sz[i],tms[i]=1,Q.push(i);
while(!Q.empty())
{
LL now=Q.front();Q.pop();
for(LL i=_top[now];i;i=_nex[i])
{
deg[_to[i]]--;
if(vis[_to[i]]) continue;
vis[_to[i]]=true;
if(val[_to[i]]<val[now]+sz[_to[i]]) val[_to[i]]=val[now]+sz[_to[i]],tms[_to[i]]=tms[now];
else if(val[_to[i]]==val[now]+sz[_to[i]]) tms[_to[i]]=(tms[_to[i]]+tms[now])%p;
if(!deg[_to[i]]) Q.push(_to[i]);
}
for(LL i=_top[now];i;i=_nex[i]) vis[_to[i]]=false;
}
}
int main()
{
n=read(),m=read(),p=read();
while(m--)
{
LL x=read(),y=read();
to[++cnt]=y,nex[cnt]=top[x],top[x]=cnt;
}
for(LL i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
for(LL i=1;i<=n;i++)
for(LL j=top[i];j;j=nex[j])
if(bel[i]!=bel[to[j]])
{
if(SS.find(make_pair(bel[i],bel[to[j]]))!=SS.end()) continue;
_to[++_cnt]=bel[to[j]],_nex[_cnt]=_top[bel[i]],_top[bel[i]]=_cnt,deg[bel[to[j]]]++;
SS.insert(make_pair(bel[i],bel[to[j]]));
}
work();
for(LL i=1;i<=js;i++)
if(ans1<val[i]) ans1=val[i],ans2=tms[i];
else if(ans1==val[i]) ans2=(ans2+tms[i])%p;
printf("%lld\n%lld",ans1,ans2);
return 0;
}

Luogu P2272 [ZJOI2007]最大半连通子图(Tarjan+dp)的更多相关文章

  1. P2272 [ZJOI2007]最大半连通子图 tarjan+DP

    思路:$tarjan+DP$ 提交:1次 题解:首先对于一个强连通分量一定是一个半连通分量,并且形成的半连通分量的大小一定是它的$size$,所以我们先缩点. 这样,我们相当于要在新的$DAG$上找一 ...

  2. BZOJ 1093: [ZJOI2007]最大半连通子图( tarjan + dp )

    WA了好多次... 先tarjan缩点, 然后题意就是求DAG上的一条最长链. dp(u) = max{dp(v)} + totu, edge(u,v)存在. totu是scc(u)的结点数. 其实就 ...

  3. luogu P2272 [ZJOI2007]最大半连通子图

    题目描述 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径.若 ...

  4. BZOJ1093: [ZJOI2007]最大半连通子图(tarjan dp)

    题意 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径.若G' ...

  5. 洛谷 P2272 [ZJOI2007]最大半连通子图 解题报告

    P2272 [ZJOI2007]最大半连通子图 题目描述 一个有向图\(G=(V,E)\)称为半连通的\((Semi-Connected)\),如果满足:\(\forall u,v \in V\),满 ...

  6. 【bzoj1093】[ZJOI2007]最大半连通子图 Tarjan+拓扑排序+dp

    题目描述 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:对于u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径. ...

  7. [luogu2272 ZJOI2007] 最大半连通子图 (tarjan缩点 拓扑排序 dp)

    传送门 题目描述 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向 ...

  8. [ZJOI2007]最大半连通子图 (Tarjan缩点,拓扑排序,DP)

    题目链接 Solution 大概是个裸题. 可以考虑到,如果原图是一个有向无环图,那么其最大半联通子图就是最长的一条路. 于是直接 \(Tarjan\) 缩完点之后跑拓扑序 DP就好了. 同时由于是拓 ...

  9. BZOJ 1093 [ZJOI2007]最大半连通子图 - Tarjan 缩点

    Description 定义一个半联通图为 : 对任意的两个点$u, v$,都有存在一条路径从$u$到$v$, 或从$v$到$u$. 给出一个有向图, 要求出节点最多的半联通子图,  并求出方案数. ...

随机推荐

  1. Python中虚拟环境venv的基本用法

    环境windows 7 venv为python3中的默认库,无需安装. 创建新的venv方法, 在当前文件夹下执行cmd,输入如下代码 python -m venv bob bob为需要创建的文件夹名 ...

  2. mui--使用mui中的图文表格组件时出现一条横线的解决方法

    最近做的微信公众号点击链接跳转到H5页面,该H5页面使用mui做的,遇到的商品列表页出现一个横线的问题, 这个是修改前的图片 解决方法: <style type="text/css&q ...

  3. 选择器zuoye

    代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title ...

  4. Windows ipconfig

    用法:    ipconfig [/allcompartments] [/? | /all |                                 /renew [adapter] | / ...

  5. git使用过程中问题

    git提交文件命令顺序 fetch merge add commit pull push 取消已add文件 git reset HEAD 文件名 覆盖本地文件 git checkout 文件名 $ g ...

  6. thinkphp 使用函数

    我们往往需要对模板输出变量使用函数,可以使用: 大理石平台支架 {$data.name|md5} 编译后的结果是: <?php echo (md5($data['name'])); ?> ...

  7. Linux下编译VLC for Android源代码总结

    转:http://blog.chinaunix.net/uid-26611383-id-3678766.html 由于项目需要,需要一个在android平台能够支持RTSP协议的播放器,由于之前没有a ...

  8. python函数使用易错举例

    关于嵌套: 嵌套使用中,  retrun inner  ---> 返回的是函数的地址 retrun inner() :    --->  运行inner()函数   ---> 运行i ...

  9. 「APIO 2019」奇怪装置

    题目 考虑推柿子 最开始的想法是如果两个\(t\)在\(mod\ B\)意义下相等,那么只需要比较一下\((t+\left \lfloor \frac{t}{B}\rfloor \right)mod\ ...

  10. POJ 1873 /// 状压+凸包

    题目大意: 国王有一片森林,巫师需要从所有树中选出一些做成围栏把其他树围起来, 每棵树都有其对应的价值 v 和能作为围栏的长度 l 要求最小价值,若存在多种最小价值的方案则选择余下长度更少的 树木较少 ...