首页 > 其他分享 >P2272 [ZJOI2007]最大半连通子图

P2272 [ZJOI2007]最大半连通子图

时间:2022-10-30 14:13:21浏览次数:56  
标签:cnt val int 子图 ++ low P2272 ZJOI2007 dis

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

题意

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

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

设 \(dis_i\) 表示到 \(i\) 这个点的最长链长度, \(S_i\) 表示到 \(i\) 的最长链的条数, \(val_i\) 表示缩完点后,\(i\) 这个强连通分量的大小,很容易想到转移方程:

\(1. dis_i > dis_j+val_i ,dis_i = dis_j+val_i,S_i=S_j.\)

\(2. dis_i == dis_j + val_i ,S_i+=S_j.\)

最后答案就是 \(ans1=max(dis_i)\) 和 \(ans2=\sum_{i=1}^{cnt}{S_i(dis_i==ans1)}.\)

一些小建议

\(1.S_i\) 要边加边模,防止炸掉。

\(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;
}

标签:cnt,val,int,子图,++,low,P2272,ZJOI2007,dis
From: https://www.cnblogs.com/jiangchenyyds/p/16841179.html

相关文章

  • 一键在Web端把CAD图自动分割成多张图纸并导出子图或图片
    前言​ 在实际中,一个CAD文件中往往存放多张图纸,有时需要这些图纸分开,单独保存或显示。以往的做法是在cad中人工进行处理。今天小编教您在web端一键把CAD图自动分割成多张......
  • C20220712T2 牛半仙的妹子图
    给定\(n\)个点和\(m\)条边,起点\(s\),每个点有颜色。给定多组\([l,r]\),求最大走\(l...r\)边权所有可以走到的不同颜色数之和。(同一种颜色在不同区间内算多组)。\(n......
  • 洛谷-P2272 最大半连通子图
    最大半连通子图tarjan缩点后计算弱连通图,相当于\(DAG\)图中点最多的路径,计算最大弱连通子图的时候就检查每个子节点的最长路径数量注意该题的答案计算与边有关,要去重......
  • matplotlib.pyplot绘制子图以及子图大小和位置的调整
    今天为了把下面的8个子图的图形调的清晰加上大小合适,花费了大概5个多小时的时间,把这段代码记录下来,以防电脑上代码丢失,制图的大小,间距、位置,颜色怎么调整,看里面的注释。很......