Jzoj 4253五校联考7day2TarjanDP并查集QYQ在艾泽拉斯

Posted SSL_ZZL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jzoj 4253五校联考7day2TarjanDP并查集QYQ在艾泽拉斯相关的知识,希望对你有一定的参考价值。

【五校联考7day2】QYQ在艾泽拉斯

link

Jzoj【4253】【五校联考7day2】QYQ在艾泽拉斯


题面

Description
在艾泽拉斯的无尽之海里,有着一群不为人知的由各个种族的冒险者统治的岛屿,这些岛屿都很庞大,足以在上面建造许多的城市,城市之间有一些单向道路连接。
有一天,QYQ无意中发现了这些岛屿,并且发现在每个城市的地下都或多或少埋藏着一些装备、金币、宝物……
可是正当QYQ兴奋不已打算全部把它们拿走时,他却惊奇的发现你的魔法在这里被限制住了,唯一可用的技能就是闪现,而且魔法只够他使用K次这个技能了,每次使用这个技能QYQ只能从一个岛屿上闪现到另外一个岛屿上。每一个岛屿只能登上一次,QYQ可以从任何一个城市开始旅程,在任何一个城市结束旅程。
城市的数量共有n个,有m条道路,每一条道路有两个参数u,v,表示从u到v有一条道路,但你只能由u到v走,两个城市属于相同的岛屿当且仅当暂时将所有道路视为双向道路时可以从其中一个城市走到另一个城市(可以途径其它城市)。
每一个城市都有一个宝物的总价值v[i],你的任务是帮助QYQ得到最大总价值的宝物,并输出这个值。

Input
从文件azeroth.in中输入数据。
输入的第一行包含两个整数n,m
输入的第二行到第m+1行,每行包含2个整数u,v,代表你可以从城市u走到城市v
输入的第m+2行包含n个整数,第i个整数代表v[i],即这个城市的宝物总价值。
输入的第m+3行包含一个整数K,代表你可以使用技能的次数。

Output
输出到文件azeroth.out中。
输出的第一行包含一个整数,代表QYQ能获得的最大的宝物总价值

Sample Input
3 2
1 2
3 1
1 2 1
0

Sample Output
4
样例说明:
QYQ从3号点开始,走到2号点,最后走到1号点,结束旅程,共获得1+2+1=4价值的宝物

Data Constraint
对于30%的数据:n<=10,K=0
对于50%的数据:n<=100,m<=100,K<=1
对于100%的数据:1<=n<=100000,1<=m<=1000000,1<=v[i]<=1000,0<=K<=100000
图中可能会有重边、自环。


解题思路

又长又臭,大无语
.
缩点
因为强连通分量里的点都是可以互相走到的,那么 缩点的价值 就是 里面所有点的价值和

.
DP
目的是为了求出路径的价值
设 f[i] 为以 i 为结尾的最大价值,因为在图上DP,所以要按拓扑顺序DP

f[a[i].to] = max(f[a[i].to], f[now] + s[a[i].to]);

.
并查集
答案价值:每个岛屿(连通块)能走的最大价值,就是这个岛屿最大的那个 f [ ] f[ ] f[]
所以用并查集,一个岛屿的点丢进同个集里,有多少个集(岛屿)就有多少个答案价值

统计答案
因为可以用k个魔法,可以走(k + 1)个岛屿
所以最终答案就是 前(k + 1)大 的 答案价值 的和


Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#define N 100100
#define M 1000100

using namespace std;

struct DT{
	int to, next;
}Ta[M], a[M];
int n, m, k, dye, print, num, Tnum, now, top, cols, anscols, x[M], y[M], v[N], fa[N];
int Ts[N], Thead[N], low[N], dfn[N], hep[N], co[N], s[N], head[N], f[N], t[N], ans[N];
queue<int> q;

void Tadd(int x, int y) { Ta[++Tnum] = (DT){y, Thead[x]}, Thead[x] = Tnum; }

void Tarjan(int x) {
	dfn[x] = low[x] = ++ now;
	hep[++top] = x;
	for(int i = Thead[x]; i; i = Ta[i].next)
		if(!dfn[Ta[i].to]) {
			Tarjan(Ta[i].to);
			low[x] = min(low[x], low[Ta[i].to]);
		} else if(!co[Ta[i].to])
			low[x] = min(low[x], dfn[Ta[i].to]);
	if(dfn[x] == low[x]) {
		co[x] = ++ cols;  //染色
		while(hep[top] != x)
			co[hep[top --]] = cols;
		-- top;
	}
}

void add(int x, int y) { a[++num] = (DT){y, head[x]}, head[x] = num; }

int find(int x) {
	if(fa[x] == x) return x;
	fa[x] = find(fa[x]);
	return fa[x];
}

void check(int x, int y) {
	x = find(x), y = find(y);
	if(x != y) fa[x] = y;
}

void DP() {
	for(int i = 1; i <= cols; i ++)
		if(!t[i]) f[i] = s[i], q.push(i);
	while(!q.empty()) {
		int now = q.front();
		q.pop();
		for(int i = head[now]; i; i = a[i].next) {
			f[a[i].to] = max(f[a[i].to], f[now] + s[a[i].to]);
			check(now, a[i].to);  //顺便并查集,我又偷个懒qwq
			
			t[a[i].to] --;
			if(!t[a[i].to]) q.push(a[i].to);
		}
	}
}

bool cmp(int k, int l) { return k > l; }

int main() {
//	freopen("azeroth.in", "r", stdin);
//	freopen("azeroth.out", "w", stdout);
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= m; i ++) {
		scanf("%d %d", &x[i], &y[i]);
		Tadd(x[i], y[i]);
	}
	for(int i = 1; i <= n; i ++)
		scanf("%d", &v[i]);
	for(int i = 1; i <= n; i ++) {
		if(!co[i]) Tarjan(i);
		fa[i] = i;  //这是并查集赋初值,我偷个懒QWQ(其实也可以不用,find判fa[x]==0就好了)
	}
	for(int i = 1; i <= n; i++)
		s[co[i]] += v[i];  //统计每个强连通分量(缩出的点)的价值
	for(int i = 1; i <= m; i ++)
		if(co[x[i]] != co[y[i]]) {
			add(co[x[i]], co[y[i]]);  //建Tarjan后新的图
			t[co[y[i]]] ++;  //统计入度
		}
	//**********************************************************
	// 以上Tarjan
	//**********************************************************
	DP();
	//**********************************************************
	// 显而易见DP
	//**********************************************************
	for(int i = 1; i <= n; i++) {
		int now = find(i);
		ans[now] = max(ans[now], f[i]);
	}
	//**********************************************************
	// 并查集求出答案价值
	//**********************************************************
	sort(ans + 1, ans + 1 + n, cmp);
	scanf("%d", &k);
	for(int i = 1; i <= k + 1; i ++)
		print += ans[i];
	// 找出前(K + 1)大的答案价值 
	printf("%d", print);
}

以上是关于Jzoj 4253五校联考7day2TarjanDP并查集QYQ在艾泽拉斯的主要内容,如果未能解决你的问题,请参考以下文章

NOIP2016提高A组五校联考4总结

NOIP2016提高A组五校联考4label

NOIP2016提高A组五校联考4square

[Tarjan][并查集][dp] Jzoj P4253 QYQ在艾泽拉斯

[拓扑排序][DP][Tarjan][并查集]JZOJ 4253 QYQ在艾泽拉斯

[jzoj]3456.NOIP2013模拟联考3恭介的法则(rule)