zoukankan      html  css  js  c++  java
  • [CF475E]Strongly Connected City 2

    题目大意:给一张$n(nleqslant2000)$个点的无向图,给所有边定向,使定向之后存在最多的有序点对$(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
    ", ans);
    	return 0;
    }
    

      

  • 相关阅读:
    MYSQL学习笔记
    javascript30--day01--Drum kit
    jQuery--dataTable 前端分页与后端分页 及遇到的问题
    hexo博客
    js—数组那些事儿
    累死青蛙系列——青蛙跳台阶问题
    js—求数组中的最大最小值
    前端html,css考点
    doxygen 使用 教程 不含安装仅设置
    fatal error LNK1169: one or more multiply defined symbols found 终极解决方案
  • 原文地址:https://www.cnblogs.com/Memory-of-winter/p/9928666.html
Copyright © 2011-2022 走看看