Jzoj P4253 QYQ在艾泽拉斯___强连通分量缩点+拓扑序dp

版权声明:欢迎借鉴,谢绝抄搬。 https://blog.csdn.net/Gx_Man_VIP/article/details/86708316

题目大意:

Q Y Q QYQ K K 次技能,每次可以从一个岛屿上闪现到另外一个岛屿上,每一个岛屿只能登上一次。
Q Y Q QYQ 能从任何一个城市开始旅程,也能在任何一个城市结束旅程。
城市的数量有 n n 个,有 m m 条有向边,两个城市属于相同的岛屿当且仅当暂时将所有道路视为双向道路时可以从其中一个城市走到另一个城市(可以途径其它城市)。
每一个城市都有一个宝物,价值 v i v_i
问得到最大总价值的宝物之和。

1 < = n < = 100000 1 < = m < = 1000000 1 < = v [ i ] < = 1000 0 < = K < = 100000 1<=n<=100000,1<=m<=1000000,1<=v[i]<=1000,0<=K<=100000
图中可能会有重边、自环。

分析:

排序去自环重边,
t a r j a n tarjan 求强连通分量并缩点,转变成有向无环图,
然后按拓扑序动规,
f_i表示以点 i i 结束,最多可以获得的总价值。 ( i ) (i为缩点后的点,即一个强连通分量)
f i = m a x ( f [ i ] ) + v i f_i=max(f[所有能到 i 的点])+v_i
然后对应的岛屿 j j 要看能不能更新收获最大值 m a x f j maxf_j
里面所有岛屿的收获的前 k + 1 k+1 大之和即为答案
对于如何求一个点对应的岛屿呢,并查集即可,然后一个强连通分量中的所有点必定在同一个岛屿,所以缩点后对应的岛屿不变。
时间复杂度 O ( m + n l o g n ) (m+nlogn)

代码:

#include <iostream>
#include <cstdio>
#include <stack>
#include <cmath>
#include <queue>
#include <cstring>
#include <algorithm>

#define N 100005
#define M 1000005

using namespace std;

struct Node { int From, To, nxt; }e[M], G[N];
struct Code { int x, y; }a[M];
int sumvalue[N], Instack[N], kuai[N], dfn[N], low[N], dis[N], opp[N], Ru[N], orz, cod, bds;  //强连通分量 
int belong[N], value[N], ls[N], f[N], cnt; //点 
int maxvalue[N], now[N], total, cdp; //岛屿 
int n, m, K, Answer;

queue <int> Q;
stack <int> st;

void Addedge(int u, int v)
{
	e[++cnt].From = u, e[cnt].To = v, e[cnt].nxt = ls[u], ls[u] = cnt;
}

void Addcder(int u, int v)
{ 
    G[++bds].To = v, G[bds].nxt = ls[u], ls[u] = bds;
}

int Find(int x)
{
	if (f[x] == x) return x;
	f[x] = Find(f[x]);
	return f[x];
}

bool cmp(Code aa, Code bb)
{
	if (aa.x == bb.x) return aa.y < bb.y;
    return aa.x < bb.x;	
}


void Tarjan(int u)
{
    dfn[u] = low[u] = ++orz;
	Instack[u] = 1;
	st.push(u);
	for (int i = ls[u]; i; i = e[i].nxt)
	{
	    int v = e[i].To;
		if (!dfn[v]) Tarjan(v), low[u] = min(low[u], low[v]);
		   else if (Instack[v]) low[u] = min(low[u], dfn[v]);
	}
	if (dfn[u] == low[u]) 
	{
	    ++cod; int yzh;
	    while ("displaylzy")
	    {
	    	yzh = st.top(); st.pop();
	    	kuai[yzh] = cod, Instack[yzh] = 0;
	    	if (yzh == u) break;
		}	
	}	
}

int main()
{
	freopen("azeroth.in", "r", stdin);
	freopen("azeroth.out", "w", stdout);
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++) f[i] = i;
	for (int i = 1; i <= m; i++) scanf("%d %d", &a[i].x, &a[i].y); 
	
	sort(a + 1, a + m + 1, cmp);
	for (int i = 1; i <= m; i++)
		if ((a[i].x != a[i].y) && (a[i].x != a[i - 1].x || a[i].y != a[i - 1].y)) 
		{
		    Addedge(a[i].x, a[i].y);
	        if (Find(a[i].x) != Find(a[i].y)) f[Find(a[i].x)] = Find(a[i].y);
 	    }
	for (int i = 1; i <= cnt; i++)
 	{
 	    int gen = Find(e[i].To);
 	    if (!belong[gen]) belong[gen] = ++cdp;
 	    belong[e[i].From] = belong[gen];
 	    belong[e[i].To] = belong[gen];
	}
	for (int i = 1; i <= n; i++) if (!belong[i]) belong[i] = ++cdp;
	for (int i = 1; i <= n; i++) if (!dfn[i]) Tarjan(i);
 	for (int i = 1; i <= n; i++) scanf("%d", &value[i]);
 	scanf("%d", &K);
	for (int i = 1; i <= n; i++) opp[kuai[i]] = belong[i], sumvalue[kuai[i]] += value[i];
		
	for (int i = 1; i <= cod; i++) ls[i] = 0;
	for (int i = 1; i <= cnt; i++)
		if (kuai[e[i].From] != kuai[e[i].To]) 
		    Addcder(kuai[e[i].From], kuai[e[i].To]), Ru[kuai[e[i].To]]++;
		
	for (int i = 1; i <= cod; i++) if (!Ru[i]) { dis[i] = sumvalue[i]; maxvalue[opp[i]] = sumvalue[i]; Q.push(i); }
	while (!Q.empty()) 
	{
		int u = Q.front(); Q.pop();
		for (int i = ls[u]; i; i = G[i].nxt)
		    {
		     	int v = G[i].To;
		     	dis[v] = max(dis[v], dis[u] + sumvalue[v]);
		     	maxvalue[opp[v]] = max(maxvalue[opp[v]], dis[v]);
		     	Ru[v]--;
		        if (!Ru[v]) Q.push(v);
		    }
	}
	for (int i = 1; i <= n; i++)
	    if (f[i] == i) now[++total] = maxvalue[belong[i]];    
	sort(now + 1, now + total + 1);
	for (int i = total; i >= max(total - K, 1); i--) Answer += now[i];
	printf("%d\n", Answer);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Gx_Man_VIP/article/details/86708316