[CF475E]Strongly Connected City 2

题目大意:给一张$n(n\leqslant2000)$个点的无向图,给所有边定向,使定向之后存在最多的有序点对$(a,b)$满足从$a$能到$b$

题解:先把边双缩点,因为这里面的点一定两两可达。

根据网上题解得知,最优解一定长这样:存在一个点$s$,使得对于任意其他点$t$,要么$s$可以到$t$,要么$t$可以到$s$,就把$s$作为根。(出题人的题解也没给出解答,就感性理解)

所以$s$的每一个子树内的边要么都朝向$s$,要么都远离$s$

然后可以枚举哪个点作为根,记$w_i$第$i$个双联通分量的大小,$sz_i$为以第$i$个双联通分量为根的子树大小,每个子树内的点在子树内的贡献为$w_i(sz_i-w_i)$,令$P$为朝向根的节点的$sz$和,过根的贡献为$P(n-w_s-P)$,所以只需要让$P$与$(n-w_s-P)$尽可能接近即可,可以用背包来实现

卡点:

 

C++ Code:

#include <cstdio>
#include <bitset>
#define maxn 2010
#define maxm (maxn * maxn)
inline int min(int a, int b) {return a < b ? a : b;}
inline int max(int a, int b) {return a > b ? a : b;}

int n, m;

namespace Graph {
	int head[maxn], cnt = 1;
	struct Edge {
		int to, nxt;
	} e[maxm];
	inline void addE(int a, int b) {
		e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt;
		e[++cnt] = (Edge) {a, head[b]}; head[b] = cnt;
	}
	
	int w[maxn];
	int DFN[maxn], low[maxn], idx;
	int S[maxn], top, res[maxn], scc;
	void tarjan(int u, int fa = 0) {
		DFN[u] = low[u] = ++idx;
		S[++top] = u;
		int v;
		for (int i = head[u]; i; i = e[i].nxt) {
			v = e[i].to;
			if (v != fa) {
				if (!DFN[v]) {
					tarjan(v, u);
					low[u] = min(low[u], low[v]);
				} else low[u] = min(low[u], DFN[v]);
			}
		}
		if (DFN[u] == low[u]) {
			scc++;
			do {
				v = S[top--];
				w[res[v] = scc]++;
			} while (u != v);
		}
	}
}

long long ans;
namespace Tree {
	int head[maxn], cnt;
	struct Edge {
		int to, nxt;
	} e[maxn << 1];
	inline void addE(int a, int b) {
		e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt;
		e[++cnt] = (Edge) {a, head[b]}; head[b] = cnt;
	}
	using Graph::res;
	using Graph::w;
	using Graph::scc;
	
	void init(int n, int m) {
		for (int i = 1; i <= n; i++) if (!Graph::DFN[i]) Graph::tarjan(i);
		for (int i = 2; i <= Graph::cnt; i += 2) {
			int u = Graph::e[i ^ 1].to, v = Graph::e[i].to;
			if (res[u] != res[v]) addE(res[u], res[v]);
		}
	}

	int sz[maxn];
	int __ans, sumsz;
	std::bitset<maxn / 2> B;
	#define ans __ans
	void dfs(int u, int fa = 0) {
		sz[u] = w[u];
		for (int i = head[u]; i; i = e[i].nxt) {
			int v = e[i].to;
			if (v != fa) {
				dfs(v, u);
				sz[u] += sz[v];
			}
		}
		ans += sz[u] * w[u];
	}
	int calc(int u) {
		B.reset();
		B[0] = true;
		ans = 0; dfs(u);
		for (int i = head[u]; i; i = e[i].nxt) {
			int v = e[i].to;
			B |= B << sz[v];
		}
		for (int i = sumsz - w[u] >> 1; i; i--) if (B[i]) return ans + i * (sumsz - w[u] - i);
		return ans;
	}
	#undef ans

	int q[maxn], h, t;
	bool vis[maxn];
	void solve(int u) {
		vis[q[h = t = 0] = u] = true;
		int res = 0;
		sumsz = 0;
		while (h <= t) {
			int u = q[h++];
			sumsz += w[u];
			for (int i = head[u]; i; i = e[i].nxt) {
				int v = e[i].to;
				if (!vis[v]) vis[q[++t] = v] = true;
			}
		}
		for (int i = 0; i <= t; i++) res = max(res, calc(q[i]));
		ans += res;
	}
	void work() {
		for (int i = 1; i <= scc; i++) if (!vis[i]) solve(i);
	}
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 0, a, b; i < m; i++) {
		scanf("%d%d", &a, &b);
		Graph::addE(a, b);
	}
	Tree::init(n, m);
	Tree::work();
	printf("%lld\n", ans);
	return 0;
}

  

posted @ 2018-11-08 14:00  Memory_of_winter  阅读(324)  评论(0编辑  收藏  举报