【YBTOJ】【Luogu P2272】[ZJOI2007]最大半连通子图

链接:

洛谷

题目大意:

一个半连通图 \(G=(V,E)\)\(\forall u,v\in V\) 满足 \(u\rightarrow v\)\(v\rightarrow u\)

给定一个图,求出它最大半连通子图及其个数。

正文:

用 Tarjan 对强连通分量缩点,问题就转化成了求新图中最长链及其个数。然后就可以用拓扑 DP 求解最长距离和方案数,设 \(f_i\) 表示以 \(i\) 为终点的方案数,\(dis_i\) 表示以 \(i\) 为终点的最长链。

不过在此之前,为了 DP 的正确性,需要对新图中的重边删去。可以在转移的过程中用数组 \(used(i)\) 表示来到 \(i\) 点时的边的起点。这样记录即可。

代码:


const int N = 1e5 + 10;

int n, m;
ll mod;

int head[N], tot;
struct edge
{
	int from, to, nxt;
}e[N * 12];
void add(int u, int v)
{
	e[++tot] = (edge){u, v, head[u]}, head[u] = tot; 
}

int dfn[N], low[N], stack[N], color[N], cnt[N], top, num, t;

void Tarjan(int u)
{
	dfn[u] = low[u] = ++num;
	stack[++top] = u;
	for (int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (!dfn[v])
		{
			Tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else
			if(!color[v])
				low[u] = min(low[u], dfn[v]);
	}
	if(low[u] == dfn[u])
	{
		t++;
		do
		{
			color[stack[top]] = t;
			cnt[t]++;
			top--;
		}while(u != stack[top + 1]);
	}
}

bool cmp (edge a, edge b)
{
	if (a.from == b.from) return a.nxt < b.nxt;
	return a.from < b.from;
}

int ans;
ll f[N], siz[N], ind[N], dis[N], used[N];
queue <int> q; 

void Topo()
{
	for (int i = 1; i <= t; i++) 
		if (!ind[i])
		{
			q.push(i);
			dis[i] = cnt[i];
			f[i] = 1;
			if (dis[ans] < dis[i]) ans = i;
		}
	while (!q.empty())
	{
		int u = q.front(); q.pop();
		for (int i = head[u]; i; i = e[i].nxt)
		{
			int v = e[i].to;
			--ind[v];
			if (!ind[v]) q.push(v);
			if (used[v] == u) continue;
			used[v] = u;
			if (dis[v] < dis[u] + cnt[v])
			{
				dis[v] = dis[u] + cnt[v];
				f[v] = 0;
				if (dis[ans] < dis[v]) ans = v;
			}
			if (dis[v] == dis[u] + cnt[v])
				f[v] = (f[v] + f[u]) % mod;
		}
	}
}

int main()
{
//	freopen(".in", "r", stdin);
//	freopen(".out", "w", stdout);
	scanf ("%d%d%lld", &n, &m, &mod);
	for (int i = 1, u, v; i <= m; ++i)
	{
		scanf ("%d%d", &u, &v);
		add(u, v);
	}
	for (int i = 1; i <= n; i++)
		if(!dfn[i]) Tarjan(i);
	// Start Removing
	tot = 0;
	memset (head, 0, sizeof head);
	for (int i = 1; i <= m; i++)
		add(color[e[i].from], color[e[i].to]);
	sort (e + 1, e + 1 + m, cmp);
	int m_ = m;
	for (int i = 1, j = 1; i <= m; i++)
		if (!(e[i].from == e[i].to) && (e[i].from != e[i - 1].from || e[i].to != e[i - 1].to))
			e[j++] = e[i];
		else m_--;
	m = m_;
	tot = 0;
	memset (head, 0, sizeof head);
	for (int i = 1; i <= m; i++)
		add(e[i].from, e[i].to), ind[e[i].to]++;
	//---
	Topo();
	ll Ans = 0;
	for (int i = 1; i <= t; i++)	
		if(dis[i] == dis[ans]) Ans = (Ans + f[i]) % mod;
	printf ("%d\n%lld", dis[ans], Ans);
    return 0;
}
posted @ 2021-02-17 20:09  Jayun  阅读(62)  评论(0编辑  收藏  举报