zoukankan      html  css  js  c++  java
  • 3.31 CodeForces练习

    F. Tourist Reform(2400)(无向图的边双连通分量)

    分析:最大的连通分量大小是答案,证明在[https://codeforces.com/blog/entry/47890]。
    然后把边的双连通分量大小改成强连通分量。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    const int N = 400005;
    const int M = 2 * N;
    int h[N], e[M], ne[M], idx;
    void add(int a, int b)
    {
    	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
    	//cout << a << "--" << b << "--" << idx - 1 << endl;
    }
    bool is_bridge[M];
    int dfn[N], low[N], timestamp;
    int stk[N], top;
    int dcc_cnt;
    int id[N];
    bool st[N];
    int au[M], av[M];
    int mx, rt;
    void tarjan(int u, int from)
    {
    	dfn[u] = low[u] = ++timestamp;
    	stk[++top] = u;
    
    	for (int i = h[u]; i != -1; i = ne[i])
    	{
    		int j = e[i];
    		if (!dfn[j])
    		{
    			tarjan(j, i);
    			low[u] = min(low[u], low[j]);
    			if (dfn[u] < low[j])
    				is_bridge[i] = is_bridge[i ^ 1] = true;
    		}
    		else if (i != (from ^ 1))
    		{
    			low[u] = min(low[u], dfn[j]);
    		}
    	}
    	if (dfn[u] == low[u])
    	{
    		int sz = 0;
    		++dcc_cnt;
    		int y;
    		do {
    			y = stk[top--];
    			id[y] = dcc_cnt;
    			++sz;
    		} while (y != u);
    		if (sz > mx) mx = sz, rt = u;
    	}
    	
    }
    
    void dfs(int u)
    {
    	st[u] = true;
    	for (int i = h[u]; i != -1; i = ne[i])
    	{
    		int j = e[i];
    		if (st[j])
    		{
    			au[i >> 1] = u;
    			av[i >> 1] = j;
    			continue;
    		}
    		if (low[j] != low[u])
    		{
    			au[i >> 1] = j;
    			av[i >> 1] = u;
    		}
    		else
    		{
    			au[i >> 1] = u;
    			av[i >> 1] = j;
    		}
    		dfs(j);
    	}
    }
    
    int main()
    {
    	int n, m;
    	scanf("%d%d", &n, &m);
    
    	memset(h, -1, sizeof h);
    	int a, b;
    	for (int i = 1; i <= m; ++i)
    	{
    		scanf("%d%d", &a, &b);
    		add(a, b), add(b, a);
    	}
    
    	tarjan(1, -1);
    	
    	/*int mx = 0;*/
    	/*for (int i = 1; i <= dcc_cnt; ++i)
    	{
    		mx = max(mx, sz[i]);
    		pos = i;
    	}*/
    
    	//最大连通分量的大小
    	printf("%d
    ", mx);
    	
    	dfs(rt);
    	for (int i = 0; i < m; ++i)
    		printf("%d %d
    ", au[i], av[i]);
    	
    	return 0;
    }
    

    F. The Shortest Statement (2200)(LCA + Dijkstra + 并查集)

    分析:这题细节居多,首先需要用和求最小生成树一样的算法kruskal生成一棵树,先不要把非树边加进图中,然后跑LCA的预处理函数,然后再把非树边加进图中,
    然后求这些非树边上的点到每个点的距离,然后再和LCA一起更新答案。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <queue>
    #include <algorithm>
    
    using namespace std;
    using LL = long long;
    using PII = pair<int, int>;
    using PLL = pair<LL, LL>;
    const LL INF64 = 1e18;
    const int N = 100005;
    const int M = 200005;
    int n, m;
    int p[N];
    struct Edge
    {
    	int a, b;
    	LL c;
    	bool use;//标记为树边还是非树边
    };
    Edge edges[M];
    Edge edges2[50];//非树边
    int find(int x)
    {
    	if (p[x] != x) p[x] = find(p[x]);
    	return p[x];
    }
    int h[N], e[M], ne[M], idx;
    LL w[M];
    LL dist[100][N];//非树边上的顶点到每个点的距离
    vector<int> vv;
    int q[N];
    int fa[N][19], depth[N];
    LL d[N];
    bool st[N];
    LL res[N];//答案
    void add(int a, int b, int c)
    {
    	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
    }
    void dfs(int u, int father)
    {
    	fa[u][0] = father;
    	depth[u] = depth[father] + 1;
    	for (int i = 1; i <= 18; ++i) fa[u][i] = fa[fa[u][i - 1]][i - 1];
    	for (int i = h[u]; i != -1; i = ne[i])
    	{
    		int j = e[i];		
    		if (j == father) continue;
    		d[j] = d[u] + w[i];
    		dfs(j, u);
    	}
    }
    int lca(int a, int b)
    {
    	if (depth[a] < depth[b])
    		swap(a, b);
    
    	for (int k = 18; k >= 0; --k)
    	{
    		if (depth[fa[a][k]] >= depth[b])
    		{
    			a = fa[a][k];
    		}
    	}
    	if (a == b) return a;
    	for (int k = 18; k >= 0; --k)
    	{
    		if (fa[a][k] != fa[b][k])
    		{
    			a = fa[a][k];
    			b = fa[b][k];
    		}
    	}
    	return fa[a][0];
    }
    void dijkstra(int s)
    {
    	for (int i = 1; i <= n; ++i) dist[s][i] = INF64;
    	memset(st, 0, sizeof st);
    	dist[s][vv[s]] = 0;
    	priority_queue<PLL, vector<PLL>, greater<PLL>> heap;
    	heap.push({ 0, vv[s] });
    
    	while (heap.size())
    	{
    		auto t = heap.top();
    		heap.pop();
    		int ver = t.second;
    		LL distance = t.first;
    		if (st[ver]) continue;
    		for (int i = h[ver]; i != -1; i = ne[i])
    		{
    			int j = e[i];
    			if (dist[s][j] > distance + w[i])
    			{
    				dist[s][j] = distance + w[i];
    				heap.push({ dist[s][j], j });
    			}
    		}
    	}
    }
    LL distance(int a, int b, int p)
    {
    	return d[a] + d[b] - 2 * d[p];
    }
    int main()
    {
    	
    	scanf("%d%d", &n, &m);
    
    	memset(h, -1, sizeof h);
    	int a, b, c;
    	for (int i = 1; i <= m; ++i)
    	{
    		scanf("%d%d%lld", &a, &b, &c);
    		//add(a, b, c), add(b, a, c);
    		edges[i] = { a, b, c };
    	}
    	for (int i = 1; i <= n; ++i) p[i] = i;
    	int cnt = 0;
    	for (int i = 1; i <= m; ++i)
    	{
    		int a = edges[i].a, b = edges[i].b;
    		a = find(a), b = find(b);
    		
    		if (a != b)
    		{
    			p[a] = b;
    			edges[i].use = true;//树边
    			++cnt;
    		}
    		if (cnt == n - 1) break;
    	}
    
    	cnt = 0;//非树边数量
    	for (int i = 1; i <= m; ++i)
    	{
    		int a = edges[i].a, b = edges[i].b;
    		LL c = edges[i].c;
    		if (!edges[i].use)
    		{			
    			edges2[++cnt] = { a, b, c };
    			vv.push_back(a), vv.push_back(b);
    		}
    		else
    		{
    			add(a, b, c), add(b, a, c);//树边
    		}
    	}
    
    	sort(vv.begin(), vv.end());
    	vv.erase(unique(vv.begin(), vv.end()), vv.end());
    
    	dfs(1, 0);
    
    	for (int i = 1; i <= cnt; ++i)
    	{
    		int a = edges2[i].a, b = edges2[i].b;
    		LL c = edges2[i].c;
    		add(a, b, c), add(b, a, c);
    	}
    
    	int q;
    	scanf("%d", &q);
    
    	//memset(dist, 0x3f, sizeof dist);
    	for (int i = 0; i < vv.size(); ++i)
    	{
    		dijkstra(i);//映射值
    	}
    
    	int u, v;
    	for (int i = 1; i <= q; ++i)
    	{
    		scanf("%d%d", &u, &v);
    		res[i] = distance(u, v, lca(u, v));
    		for (int j = 0; j < vv.size(); ++j)
    		{
    			res[i] = min(res[i], dist[j][u] + dist[j][v]);
    		}
    	}
    
    	for (int i = 1; i <= q; ++i)
    	{
    		printf("%lld
    ", res[i]);
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    一本通 1259:【例9.3】求最长不下降序列
    一本通 1258:【例9.2】数字金字塔
    洛谷 P1198 [JSOI2008]最大数
    洛谷 P2863 [USACO06JAN]牛的舞会The Cow Prom
    【BZOJ1062】糖果雨(NOI2008)-数形结合+二维树状数组
    【BZOJ4070】雅加达的摩天楼(APIO2015)-分块+最短路
    【BZOJ2326】数学作业(HNOI2011)-递推+矩阵快速幂
    【BZOJ2734】集合选数(HNOI2012)-状压DP
    【BZOJ3213】抛硬币(ZJOI2013)-期望DP+KMP+高精度
    【BZOJ3590】Quare(SNOI2013)-状压DP
  • 原文地址:https://www.cnblogs.com/pixel-Teee/p/12604257.html
Copyright © 2011-2022 走看看