zoukankan      html  css  js  c++  java
  • 【YBTOJ】【Luogu P2272】[ZJOI2007]最大半连通子图

    链接:

    洛谷

    题目大意:

    一个半连通图 (G=(V,E))(forall u,vin V) 满足 (u ightarrow v)(v ightarrow 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
    %lld", dis[ans], Ans);
        return 0;
    }
    
  • 相关阅读:
    iOS
    iOS
    iOS
    iOS
    iOS
    iOS
    iOS
    iOS
    iOS
    iOS
  • 原文地址:https://www.cnblogs.com/GJY-JURUO/p/14409583.html
Copyright © 2011-2022 走看看