bzoj1093 [ZJOI2007]最大半连通子图 强连通分量+拓扑排序+dp

19 篇文章 0 订阅
14 篇文章 0 订阅

Description


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

N ≤100000, M ≤1000000;对于100%的数据, X ≤10^8

Solution


很显然强连通分量里面的点都是可以选的。考虑缩点后图变成一个dag,我们要找的就是最长链和最长链计数
考虑很sb的dp,对于长度相等的跟新数量,对于长度更长的更新长度和数量就没了。。

非常sb的问题在于这题会有重边,然后会算重。。。我比较zz就开map屮过去了。。。
不得不说数据真的墙

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <map>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

const int N=200005;
const int E=4000005;

struct edge {int x,y,next;} e[E];

std:: map <int,bool> map[N];

int low[N],dfn[N],bel[N],size[N],len[N],cnt[N];
int stack[N],queue[N],top,head,tail;
int ls[N],d[N],edCnt,max,ans,p;
int fa[N];

bool vis[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void add_edge(int x,int y) {
	e[++edCnt]=(edge) {x,y,ls[x]}; ls[x]=edCnt; d[y]++;
}

void dfs1(int now) {
	dfn[now]=low[now]=++dfn[0];
	vis[now]=true; stack[++top]=now;
	for (int i=ls[now];i;i=e[i].next) {
		if (!dfn[e[i].y]) {
			dfs1(e[i].y);
			low[now]=std:: min(low[now],low[e[i].y]);
		} else if (vis[e[i].y]) {
			low[now]=std:: min(low[now],dfn[e[i].y]);
		}
	}
	if (dfn[now]==low[now]) {
		int y=0; ++bel[0];
		for (;y!=now;) {
			y=stack[top--];
			bel[y]=bel[0]; vis[y]=false;
			++size[bel[0]];
		}
	}
}

void dfs2(int now,int tot) {
	if (tot>max) max=tot,ans=0;
	if (max==tot) {
		ans++; (ans>=p)?(ans-=p):0;
	}
	for (int i=ls[now];i;i=e[i].next) {
		dfs2(e[i].y,tot+size[e[i].y]);
	}
}

void mod(int &x) {
	(x>=p)?(x-=p):0;
}

int find(int x) {
	return !fa[x]?x:(fa[x]=find(fa[x]));
}

bool merge(int x,int y) {
	x=find(x),y=find(y);
	if (x==y) return false;
	fa[x]=y;
	return true;
}

int main(void) {
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	int n=read(),m=read(); p=read();
	rep(i,1,m) {
		int x=read(),y=read();
		add_edge(x,y);
	}
	rep(i,1,n) if (!dfn[i]) dfs1(i);
	rep(i,1,n) d[i]=ls[i]=0;
	rep(i,1,m) {
		if (bel[e[i].x]!=bel[e[i].y]) {
			if (map[bel[e[i].x]][bel[e[i].y]]) continue;
			map[bel[e[i].x]][bel[e[i].y]]=true;
			add_edge(bel[e[i].x],bel[e[i].y]);
		}
	}
	rep(i,1,bel[0]) len[i]=size[i],cnt[i]=1;
	head=1,tail=0;
	rep(i,1,bel[0]) if (!d[i]) {
		queue[++tail]=i;
	}
	for (;head<=tail;) {
		int now=queue[head++];
		if (len[now]>max) max=len[now],ans=cnt[now];
		else if (len[now]==max) mod(ans+=cnt[now]);
		for (int i=ls[now];i;i=e[i].next) {
			if (len[now]+size[e[i].y]>len[e[i].y]) {
				len[e[i].y]=len[now]+size[e[i].y];
				cnt[e[i].y]=cnt[now];
			} else if (len[now]+size[e[i].y]==len[e[i].y]) {
				mod(cnt[e[i].y]+=cnt[now]);
			}
			if (!(--d[e[i].y])) {
				queue[++tail]=e[i].y;
			}
		}
	}
	printf("%d\n%d\n", max,ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值