【题解】[ZJOI2007]最大半连通子图
作者:互联网
\(\text{Solution:}\)
首先考虑何时满足题目中所说的最大半连通子图。
先把强连通分量缩起来应该是毋庸置疑的一步了。考虑如何从一个强连通分量来拓展到半连通分量。
推论1:如果一张缩完点的图是半连通图,那么它的拓扑序一定唯一。
\(Proof:\) 假定拓扑序不唯一,也就是在 \(bfs\) 的过程中,队列同时存在了两个点,那么这两个点必然是无法相互到达的。证毕。
那么,有了这么一个推论,我们就可以考虑如何解题了:先考虑如何计算最大的节点数。我们缩完点之后令强连通分量的 \(siz\) 为其连通块大小,问题就转化为了有向图求最长链了。
那么方案数咋求?考虑设 \(f_i,g_i\) 分别表示到点 \(i\) 的最大节点数和方案数。如果更新了 \(f\) 就把 \(g\) 置为 \(0,\) 否则直接累加。比当前 \(f\) 小的就可以跳过了。
注意边的去重。可以去枚举原图每次清空数组做到无 $\log $ 判重,但是笔者比较懒直接上 map
了。
注意,计算 \(siz\) 不能取模,更新 \(g\) 的时候要取模。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=4e5+10;
const int M=4e6+10;
struct E{int nxt,to;}e[M],edge[M];
int head[N],Head[N],tot,tto,mod;
map<int,map<int,int> >mp;
inline int read(){
char ch=getchar();int nn=0,ssss=1;
while(ch<'0'||ch>'9'){if(ch=='-')ssss*=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){nn=nn*10+(ch-'0');ch=getchar();}
return nn*ssss;
}
inline int Add(int x,int y){return (x+y+mod)%mod;}
inline int Mul(int x,int y){return 1ll*x*y%mod;}
inline void link(int x,int y,int w=0){
if(w){
edge[++tto]=(E){Head[x],y};
Head[x]=tto;
return;
}
e[++tot]=(E){head[x],y};
head[x]=tot;
}
int dfn[N],low[N],st[N],top,inst[N],dfstime;
int c[N],scc,siz[N],in[N],n,m;
inline int Max(int x,int y){return x>y?x:y;}
inline int Min(int x,int y){return x<y?x:y;}
void tarjan(int x){
low[x]=dfn[x]=++dfstime;
inst[x]=1;st[++top]=x;
for(int i=head[x];i;i=e[i].nxt){
int j=e[i].to;
if(!dfn[j]){
tarjan(j);
low[x]=Min(low[x],low[j]);
}
else if(inst[j])low[x]=Min(low[x],dfn[j]);
}
if(dfn[x]==low[x]){
int y=-1;
++scc;
while(y=st[top--]){
siz[scc]++;
c[y]=scc;
inst[y]=0;
if(x==y)break;
}
}
}
struct Rem{int u,v;}rem[M];
int f[N],g[N];
void BFS(){
queue<int>q;
for(int i=1;i<=scc;++i)if(!in[i])q.push(i);
for(int i=1;i<=scc;++i)if(!in[i])f[i]=siz[i],g[i]=1;
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=Head[x];i;i=edge[i].nxt){
int j=edge[i].to;
in[j]--;
if(!in[j])q.push(j);
int v=f[x]+siz[j];
if(v<f[j])continue;
if(v>f[j]){
f[j]=v;
g[j]=g[x];
continue;
}
if(v==f[j]){
g[j]+=g[x];
g[j]%=mod;
continue;
}
}
}
}
void solve(){
int mx=-1;
for(int i=1;i<=scc;++i)mx=Max(mx,f[i]);
printf("%lld\n",mx);
int res=0;
for(int i=1;i<=scc;++i)if(f[i]==mx)res+=g[i],res%=mod;
printf("%lld\n",res);
}
signed main(){
freopen("semi5.in","r",stdin);
n=read();m=read();mod=read();
for(int i=1;i<=m;++i){
int x=read(),y=read();
link(x,y);
rem[i]=(Rem){x,y};
}
for(int i=1;i<=n;++i)if(!dfn[i])top=0,tarjan(i);
for(int i=1;i<=m;++i){
int u=c[rem[i].u];
int v=c[rem[i].v];
if(u==v)continue;
if(mp[u][v])continue;
in[v]++;
mp[u][v]=1;
link(u,v,1);
}
BFS();
solve();
return 0;
}
标签:连通,ch,int,题解,子图,ZJOI2007,考虑,分量 来源: https://www.cnblogs.com/h-lka/p/15334077.html