题解 P3627 【[APIO2009]抢掠计划】
原题链接【[APIO2009]抢掠计划】
缩点,水题一道~~
题意
大概就是要求一个从 1 开始的路径,
要求结束的一个点是有酒吧,
路径上点可重复经过,但同一个点的点权只计算一次。
这一看就是缩点了啦
可重复经过~~,
这句话经常就是我们判断一道图上的题是否就是 tarjan 水题的标准
那么本题,缩完点了,建完新图了,然后呢~~
一个简单的想法是在新图上跑一边最长路(和最短路几乎一样)
当然你也可以换成 dp , 或者记搜什么的
然后在所有的有酒吧的连通块里选择一个最优的
新问题来了,怎么跑最短路
点权转边权~~
怎么个转法?
我觉得我的思路不错,因此发了这篇题解
拆点,把一个点拆成两个点,该边边权即为该点点权
然后跑 spfa 最长路就好了
给出代码
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
const int maxn = 5e5 + 10;
const int INF = 1 << 25;
inline int read() {
char c = getchar(); int x = 0, f = 1;
for (; c > '9' || c < '0'; c = getchar()) if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return x * f;
}
int a[maxn], id[maxn], sum[maxn], stack[maxn], top, gg, k;
int dis[maxn << 1], ver[maxn], nxt[maxn], head[maxn], tot;
vector <int> h[maxn << 1], hh[maxn << 1];
int p[maxn], dfn[maxn], low[maxn], cnt;
bool ins[maxn], vis[maxn << 1];
int n, m, u, v, S, P, ans;
queue <int> q;
void add_edge () {
u = read(), v = read();
ver[++ tot] = v;
nxt[tot] = head[u];
head[u] = tot;
}
void tarjan (int x) {
dfn[x] = low[x] = ++ cnt;
stack[++ top] = x;
ins[x] = 1;
for (register int i = head[x]; i; i = nxt[i]) {
if (!dfn[ver[i]])
tarjan (ver[i]), low[x] = min (low[x], low[ver[i]]);
else if (ins[ver[i]])
low[x] = min (low[x], dfn[ver[i]]);
}
if (dfn[x] == low[x]) {
++ gg;
do {
k = stack[top --];
ins[k] = 0;
id[k] = gg;
sum[gg] += a[k];
} while (x != k);
}
}
void djj () {
n = read(), m = read();
for (; m; m --)
add_edge ();
for (register int i = 1; i <= n; i ++)
a[i] = read();
for (register int i = 1; i <= n; i ++)
if (!dfn[i]) tarjan (i);
S = read(), P = read();
for (register int i = 1; i <= P; i ++)
p[i] = read();
}
void lxy () {
for (register int i = 1; i <= n; i ++)
for (register int j = head[i]; j; j = nxt[j])
if (id[i] != id[ver[j]])
h[id[i] + gg].push_back (id[ver[j]]), hh[id[i] + gg].push_back (0);
for (register int i = 1; i <= gg; i ++)
h[i].push_back (i + gg), hh[i].push_back (sum[i]);
memset (dis, - INF, sizeof dis);
vis[id[S]] = 1, dis[id[S]] = 0;
q.push (id[S]);
for (; !q.empty (); ) {
int u = q.front ();
q.pop (), vis[u] = 0;
for (register int i = 0; i < h[u].size(); i ++) {
int v = h[u][i], w = hh[u][i];
if (dis[v] < dis[u] + w) {
dis[v] = dis[u] + w;
if (!vis[v]) {
q.push (v);
vis[v] = 1;
}
}
}
}
for (register int i = 1; i <= P; i ++)
ans = max (ans, dis[id[p[i]] + gg]);
printf ("%d\n", ans);
}
void djj_lxy () {
djj (), lxy ();
}
int main() {
djj_lxy ();
}
/* 样例,本题样例还算不错了
6 7
1 2
2 3
3 5
2 4
4 1
2 6
6 5
10
12
8
16
1
5
1 4
4 3 5 6
47
*/
本题就完了,完结撒花~~
距离新 Noip 还有不到 2 个月的时间,祝大家 rp ++
也希望自己能更努力一些,加油!