图论1-图的连通性相关
luogu P2272 最大半连通子图
- 题意描述:一个有向图 \(G=\left(V,E\right)\)称为半连通的 ,如果满足:\(\forall u,v\in V\),存在一条 \(u\) 到 \(v\) 的有向路径或者从 \(v\) 到 \(u\) 的有向路径。称\(G'=(V',E)\)是\(G\)的导出子图如有\(V'\)在\(G\)中所有有关边均在\(G'\)中。若G′是G的导出子图,且G′半连通,则称G‘为G的半连通子图。若 G’ 是 G 所有半连通子图中包含节点数最多的,则称G′是G的最大半连通子图。给定有向图\(G\),求其最大半连通子图的节点数\(K\)以及不同的最大半连通子图的数目\(C\)
- 因对于一个强连通分量而言,若可以存在一条路径\(uv\)连接强连通分量外的点\(u\),与强连通分量内的点\(v\),则显然\(u\)可以到达该强连通分量内的任何点。所以若是选取强连通分量中的一个点,显然可以选取整个强连通分量。所以我们可以通过\(tarjan\)算法缩点来创造一个\(DAG\)
- 得到\(DAG\)后,因为\(tarjan\)算法所得到的强连通分量编号逆序即为拓扑序,我们直接对强连通分量重新以链式前向星建图,并且在建图的过程中通过\(hash\)来去除重边,避免对结果的计算产生影响。建图之后强连通分量编号即为图中顶点的编号。
- 而因为我们所求的最大半连通子图中任意两点之间都有路径,所以我们所求的最大半连通子图只能是所得拓扑图中的一条链。
- 建图之后我们按照拓扑序遍历所有节点,采用类似动态规划的方式进行答案的求解,规定函数\(f\)记录下当前子图的节点数目,\(g\)记录下与当前子图节点数目相同情况下的方案数
- 状态转移方程
\[g(k)= \left\{\begin{array}{lcl}
g(i),&f(k)<f(i)+size(i)\\
g(i)+g(k),&f(k)=f(i)+size(i)
\end{array}\right.
\\
f(k)= \left\{\begin{array}{lcl}
f(i)+size(i),&f(k)<f(i)+size(i)\\
f(i) ,&else
\end{array}\right.
\]
- 其中\(i\),\(k\)之间存在边\(i\to k\)
- 所以我们有代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5,maxm=1e6+5;
// 强连通分量算法完成后即可得到拓扑序,不需要再进行拓扑排序
#define ll long long
int n,m,mod;
int tim=0,top=0,tot=0;;
int dfn[maxn],low[maxn],s[maxn],vis[maxn],id[maxn],size[maxn];
struct edge
{
int u,v,nxt;
}e[maxm],et[maxm];
int head[maxm],headt[maxm];
int cnt=0,cntt=0;
void addedge(int u,int v){
e[++cnt].u=u;
e[cnt].v=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
void tarjan(int u){
low[u]=dfn[u]=++tim;
s[++top]=u;
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt)
{
int 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]){
int y;
tot++;
do {
y = s[top--];
vis[y] = 0;
id[y] = tot;
size[tot] ++ ;
} while (y != u);
}
}
int main(){
scanf("%d %d %d",&n,&m,&mod);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
}
for(int i=1;i<=n;i++){
if(!dfn[i])tarjan(i);
}
unordered_set<ll> st;
for(int u=1;u<=n;u++){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
int a=id[u],b=id[v];
ll hasher = a*1000000ll+b;
if(a!=b && !st.count(hasher)){
st.insert(hasher);
et[++cntt].u=a;
et[cntt].v=b;
et[cntt].nxt=headt[a];
headt[a]=cntt;
}
}
}
int f[maxn],g[maxn];
for(int i=tot;i;i--){
if(!f[i]){
f[i]=size[i];
g[i]=1;
}
for(int j=headt[i];j;j=et[j].nxt){
int k=et[j].v;
if(f[k]<f[i]+size[k])
{
f[k]=f[i]+size[k];
g[k]=g[i];
}else if(f[k]==f[i]+size[k]){
g[k]=(g[k]+g[i])%mod;
}
}
}
int maxe=0,sum=0;
for(int i=1;i<=tot;i++){
if(f[i]>maxe){
maxe=f[i];
sum=g[i];
}else if(f[i]==maxe)sum=(sum+g[i])%mod;
}
cout<<maxe<<endl;
cout<<sum<<endl;
return 0;
}

浙公网安备 33010602011771号