zoukankan      html  css  js  c++  java
  • [USACO15JAN]Grass Cownoisseur G

    传送门


    比赛的时候tarjan算法出锅了,赶快来复习一下。

    题意:给一个\(n\)个点\(m\)条边的有向图,其中可以选择一条边逆行一次,问在这个基础上如何选择一条回路,使其经过的点最多。(重复经过的点只算一次,\(n,m \leqslant 10^5\))。


    首先,一个强连通分量内的点都可以选,那么不妨先用tarjan算法缩点,新图中每个点的权值就是原图中该连通分量中点的个数。

    那么问题变成了,如何在一个DAG上选择一条从节点\(x\)开始点权和尽量大的回路,其中可以有一条边逆行一次。


    因此可以枚举这条边,对于一条边\(u,v\),只要求出从\(x\)\(u\),和从\(v\)\(x\)的点权和最大的路径。拿\(x\)\(u\)为例,题解中几乎都用的是spfa来求解,但是spfa算法本身的时间复杂度是不稳定的。实际上因为这个图是DAG,因此可以用拓扑排序解决。只不过要拓扑排序两次,先是处理出所有\(x\)走不到的节点,再是从\(x\)开始拓扑排序并更新答案。

    至于从\(v\)\(x\),建反图,用同样的算法即可解决。


    这题还有一种做法是分层图,将新图复制一分,并且如果有边\(u \to v\),就再连边\(v \to u + n\),代表逆行一次。这样同时也能满足走到第二层后就回不到第一层了,刚好符合只能逆行一次的限制。而且分层图同样是DAG,用拓扑排序来解决最长路问题即可。

    #include<bits/stdc++.h>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    #define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const db eps = 1e-8;
    const int maxn = 1e5 + 5;
    In ll read()
    {
    	ll ans = 0;
    	char ch = getchar(), las = ' ';
    	while(!isdigit(ch)) las = ch, ch = getchar();
    	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
    	if(las == '-') ans = -ans;
    	return ans;
    }
    In void write(ll x)
    {
    	if(x < 0) x = -x, putchar('-');
    	if(x >= 10) write(x / 10);
    	putchar(x % 10 + '0');
    }
    
    int n, m;
    struct Edge
    {
    	int nxt, to;
    }e[maxn];
    int head[maxn], ecnt = -1;
    In void addEdge(int x, int y)
    {
    	e[++ecnt] = (Edge){head[x], y};
    	head[x] = ecnt;
    }
    
    int dfn[maxn], low[maxn], cnt = 0;
    bool in[maxn];
    int st[maxn], top = 0, col[maxn], num[maxn], ccol = 0;
    In void tarjan(int now)
    {
    	dfn[now] = low[now] = ++cnt;
    	st[++top] = now, in[now] = 1;
    	forE(i, now, v)
    	{
    		if(!dfn[v])
    		{
    			tarjan(v);
    			low[now] = min(low[now], low[v]);
    		}
    		else if(in[v]) low[now] = min(low[now], dfn[v]);
    	}
    	if(low[now] == dfn[now])
    	{
    		int x; ++ccol;
    		do
    		{
    			x = st[top--], in[x] = 0;
    			col[x] = ccol, num[ccol]++;
    		}while(x != now);
    	}
    }
    
    Edge e2[maxn];
    int head2[maxn], ecnt2 = -1, du[maxn];
    In void addEdge2(int x, int y)
    {
    	e2[++ecnt2] = (Edge){head2[x], y};
    	head2[x] = ecnt2;
    	du[y]++;
    }
    In void buildGraph(bool flg)
    {
    	Mem(head2, -1), ecnt2 = -1, Mem(du, 0);
    	for(int u = 1; u <= n; ++u)
    		forE(i, u, v)
    			if(col[u] ^ col[v]) flg ? addEdge2(col[u], col[v]) : addEdge2(col[v], col[u]);
    }
    
    int dis1[maxn], dis2[maxn];
    /*bool vis[maxn];
    In void spfa(int* dis)
    {
    	Mem(vis, 0);
    	queue<int> q; q.push(col[1]); dis[col[1]] = num[col[1]];
    	while(!q.empty())
    	{
    		int now = q.front(); q.pop();
    		vis[now] = 0;
    		for(int i = head2[now], v; ~i && (v = e2[i].to); i = e2[i].nxt) 
    		{
    			if(dis[v] < dis[now] + num[v])
    			{
    				dis[v] = dis[now] + num[v];
    				if(!vis[v]) q.push(v), vis[v] = 1;
    			}
    		}
    	}
    }*/
    In void topo(int* dis)
    {
    	queue<int> q;
    	for(int i = 1; i <= ccol; ++i) if(!du[i] && i != col[1]) q.push(i);
    	while(!q.empty())
    	{
    		int now = q.front(); q.pop();
    		for(int i = head2[now], v; ~i && (v = e2[i].to); i = e2[i].nxt) 
    			if(!--du[v] && v != col[1]) q.push(v);
    	}
    	q.push(col[1]), dis[col[1]] = num[col[1]];
    	while(!q.empty())
    	{
    		int now = q.front(); q.pop();
    		for(int i = head2[now], v; ~i && (v = e2[i].to); i = e2[i].nxt) 
    		{
    			dis[v] = max(dis[v], dis[now] + num[v]);
    			if(!--du[v]) q.push(v);
    		}
    	}
    }
    
    int main()
    {
    	Mem(head, -1), ecnt = -1;
    	n = read(), m = read();
    	for(int i = 1; i <= m; ++i)
    	{
    		int x = read(), y = read();
    		addEdge(x, y); 
    	}
    	for(int i = 1; i <= n; ++i) if(!dfn[i]) tarjan(i);
    	buildGraph(1), topo(dis1);
    	buildGraph(0), topo(dis2);
    	int ans = 0;
    	for(int u = 1; u <= n; ++u)
    		forE(i, u, v)
    			if(col[u] ^ col[v])
    			{
    				if(!dis1[col[v]] || !dis2[col[u]]) continue;
    				ans = max(ans, dis1[col[v]] + dis2[col[u]] - num[col[1]]);	
    			} 
    	write(ans), enter;
    	return 0;
    }
    
  • 相关阅读:
    Nginx安装
    node.js搭建vue脚手架
    Oracle引入数据
    MVC引入Junit单元测试
    Git版本控制器
    IDEA-Maven
    SSM框架整合
    【测试基础第五篇】测试用例编写和评审
    【测试基础第四篇】测试用例设计方法
    【测试基础第三篇】需求测试分析
  • 原文地址:https://www.cnblogs.com/mrclr/p/15590719.html
Copyright © 2011-2022 走看看