为了能到远方,脚下的每一步都不能少.|

Aurora-JC

园龄:3年1个月粉丝:3关注:4

📂题解
🔖tarjan
2022-10-30 14:03阅读: 49评论: 0推荐: 0

P2272 [ZJOI2007]最大半连通子图

哎,这道题打了半个小时,调了两个小时,最后发现竟然是把 Tarjanwhile 给打成 if ,呜呜,枉费我两个小时时间,所以下次一定要记住不能打成 if (估计也就我一个人吧)

题意

定义最大半连通图:对于图中任意两点 u,v ,存在一条 uv 的有向路径 或者 从 vu 的有向路径。求一个图中不同的最大半连通子图的大小和数目。

看到题目时应该很容易想到,如果两点互相可以到达,那么它们必是半连通图,所以考虑先 Tarjan 缩点,缩完点后,删去重边,图就成为 DAG,然后我们只要对这个图求一个最长链的长度和条数,就可以用拓扑进行 dp

disi 表示到 i 这个点的最长链长度, Si 表示到 i 的最长链的条数, vali 表示缩完点后,i 这个强连通分量的大小,很容易想到转移方程:

1.disi>disj+vali,disi=disj+vali,Si=Sj.

2.disi==disj+vali,Si+=Sj.

最后答案就是 ans1=max(disi)ans2=i=1cntSi(disi==ans1).

一些小建议

1.Si 要边加边模,防止炸掉。

2.Tarjan 缩点后的点的排列顺序是逆拓扑序,所以不需要对新图进行拓扑排序 ,直接倒序做就行。

3. 要判重边,不然会死得很惨。

code

#include<bits/stdc++.h>
#define N 1000005
#define M 10000005
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,m,mod,tot,t,cnt,dis[N],maxn,ss,top;
int dfn[N],low[N],s[N],vis[N],gp[N];
int Head[N],to[M],Next[M],val[N],S[N],pre[N];
map<pair<int,int>,bool> H;
void add(int u,int v){to[++tot]=v,pre[tot]=u,Next[tot]=Head[u],Head[u]=tot;}
void tarjan(int x){
dfn[x]=low[x]=++t,vis[x]=1,s[++top]=x;
for(int i=Head[x];i;i=Next[i]){
int y=to[i];
if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
else if(vis[y]) low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x]){
int k=s[top--];cnt++;
while(x!=k){
gp[k]=cnt,val[cnt]++,vis[k]=0;
k=s[top--];
}
gp[x]=cnt,val[cnt]++,vis[x]=0,dis[cnt]=val[cnt],S[cnt]=1;
}
}
int main(){
n=read(),m=read(),mod=read();
for(int i=1;i<=m;++i){
int u=read(),v=read();add(u,v);
}
for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
m=tot;
tot=0,memset(Head,0,sizeof(Head));
for(int i=1;i<=m;++i){
int x=gp[pre[i]],y=gp[to[i]];
if(x==y) continue;
if(!H[make_pair(x,y)])
add(x,y),H[make_pair(x,y)]=1;
}
for(int x=cnt;x;--x){
for(int i=Head[x];i;i=Next[i]){
int y=to[i];
if(dis[y]<dis[x]+val[y]) dis[y]=dis[x]+val[y],S[y]=S[x];
else if(dis[y]==dis[x]+val[y]) S[y]=(S[y]+S[x])%mod;
}
}
for(int i=1;i<=cnt;++i){
if(dis[i]>maxn) maxn=dis[i],ss=S[i];
else if(dis[i]==maxn) ss+=S[i],ss%=mod;
}
printf("%d\n%d\n",maxn,ss);
return 0;
}

本文作者:南风未起

本文链接:https://www.cnblogs.com/jiangchen4122/p/16841179.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Aurora-JC  阅读(49)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起