[bzoj1093][ZJOI2007]最大半连通子图

题目大意:一个有向图称为半连通的,当且仅当对于任意两点$u,v$,都满足$u$能到达$v$或者$v$能到达$u$。给一张有向图,问该图最大半连通子图的节点个数及方案数。

题解:发现任意一个强连通分量显然都是半连通子图。把它们都缩成一个点。形成一个$DAG$,问题就转化为了找最长链长度及方案数。$DP$即可

卡点:方案数忘取模

 

C++ Code:

#include <cstdio>
#include <algorithm>
#define maxn 100010
#define maxm 1000010

#define ONLINEJUDGE
#define read() R::READ()
#include <cctype>
namespace R {
    int x;
    #ifdef ONLINE_JUDGE
    char *ch, op[1 << 26];
    inline void init() {
        fread(ch = op, 1, 1 << 26, stdin);
    }
    inline int READ() {
        while (isspace(*ch)) ch++;
        for (x = *ch & 15, ch++; isdigit(*ch); ch++) x = x * 10 + (*ch & 15);
        return x;
    }
    #else
    char ch;
    inline int READ() {
        ch = getchar();
        while (isspace(ch)) ch = getchar();
        for (x = ch & 15, ch = getchar(); isdigit(ch); ch = getchar()) x = x * 10 + (ch & 15);
        return x;
    }
    #endif
}

int n, m, mod, ans, num;
inline void getans(int a, int b) {
	if (ans == a) (num += b) %= mod;
	if (ans < a) ans = a, num = b;
}
inline int min(int a, int b) {return a < b ? a : b;}

namespace Tree {
	int sz[maxn], ind[maxn], CNT;
	
	int head[maxn], cnt;
	struct Edge {
		int to, nxt;
	} e[maxm];
	void add(int a, int b) {
		e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt;
		ind[b]++;
	}
	
	int q[maxn], h = 1, t = 0;
	int f[maxn], num[maxn];
	void bfs() {
		for (int i = 1; i <= CNT; i++) if (!ind[i]) {
			q[++t] = i;
			f[i] = sz[i];
			num[i]++;
			getans(f[i], num[i]);
		}
		while (h <= t) {
			int u = q[h++];
			for (int i = head[u]; i; i = e[i].nxt) {
				int v = e[i].to;
				if (f[v] < f[u] + sz[v]) {
					f[v] = f[u] + sz[v];
					num[v] = num[u];
				} else if (f[v] == f[u] + sz[v]) (num[v] += num[u]) %= mod;
				if (!--ind[v]) {
					getans(f[v], num[v]);
					q[++t] = v;
				}
			}
		}
	}
}
namespace Graph {
	int res[maxn];
	int head[maxn], cnt;
	struct Edge {
		int from, to, nxt;
		inline bool operator < (const Edge &rhs) const{
			if (res[from] == res[rhs.from]) return res[to] < res[rhs.to];
			else return res[from] < res[rhs.from];
		}
	} e[maxm];
	void add(int a, int b) {
		e[++cnt] = (Edge) {a, b, head[a]}; head[a] = cnt;
	}
	
	int DFN[maxn], low[maxn], idx;
	int S[maxn], top;
	bool ins[maxn];
	void Tarjan(int u) {
		DFN[u] = low[u] = ++idx;
		ins[S[++top] = u] = true;
		int v;
		for (int i = head[u]; i; i = e[i].nxt) {
			v = e[i].to;
			if (!DFN[v]) {
				Tarjan(v);
				low[u] = min(low[u], low[v]);
			} else if (ins[v]) low[u] = min(low[u], DFN[v]);
		}
		if (low[u] == DFN[u]) {
			Tree::CNT++;
			do {
				ins[v = S[top--]] = false;
				Tree::sz[res[v] = Tree::CNT]++;
			} while (v != u); 
		}
	}
	inline void tarjan(int n) {
		for (int i = 1; i <= n; i++) if (!DFN[i]) Tarjan(i);
	}
	
	void init() {
		std::sort(e + 1, e + cnt + 1);
		for (int i = 1; i <= cnt; i++) {
			if (res[e[i].from] == res[e[i - 1].from] && res[e[i].to] == res[e[i - 1].to]) continue;
			if (res[e[i].from] != res[e[i].to]) Tree::add(res[e[i].from], res[e[i].to]);
		}
	}
}

int main() {
    #ifdef ONLINE_JUDGE
    R::init();
    #endif
    n = read(), m = read(), mod = read();
	for (int i = 1; i <= m; i++) {
		int a = read(), b = read();
		Graph::add(a, b);
	}
	Graph::tarjan(n);
	Graph::init();
	Tree::bfs();
	printf("%d\n%d\n", ans, num);
	return 0;
}

 

posted @ 2018-09-17 13:07  Memory_of_winter  阅读(160)  评论(0编辑  收藏  举报