[Luogu] P3627 [APIO2009]抢掠计划
Description
Siruseri城中的道路都是单向的。不同的道路由路口连接。按照法律的规定,在每个路口都设立了一个Siruseri 银行的 ATM 取款机。令人奇怪的是,Siruseri 的酒吧也都设在路口,虽然并不是每个路口都设有酒吧。
Banditji 计划实施 Siruseri 有史以来最惊天动地的 ATM 抢劫。他将从市中心出发,沿着单向道路行驶,抢劫所有他途径的 ATM 机,最终他将在一个酒吧庆祝他的胜利。
使用高超的黑客技术,他获知了每个 ATM 机中可以掠取的现金数额。他希望你帮助他计算从市中心出发最后到达某个酒吧时最多能抢劫的现金总数。他可以经过同一路口或道路任意多次。但只要他抢劫过某个ATM机后,该 ATM 机里面就不会再有钱了。
Solution
缩点后,用拓扑排序跑一遍最长路即可。新的点值即为这一强连通分量中所有的金额之和。
注意终点要是酒吧。
Code
#include <bits/stdc++.h>
using namespace std;
queue < int > q;
int n, m, s, p, tot, tot1, top, ind, cnt, res, ok[500005], sum[500005], col[500005], bar[500005], money[500005], hd[500005], to[500005], nxt[500005], low[500005], dfn[500005], que[500005], vis[500005], hd1[500005], to1[500005], nxt1[500005], dis[500005], rd[500005];
int read()
{
int x = 0, fl = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * fl;
}
void add(int u, int v)
{
tot ++ ;
to[tot] = v;
nxt[tot] = hd[u];
hd[u] = tot;
return;
}
void add1(int u, int v)
{
tot1 ++ ;
to1[tot1] = v;
nxt1[tot1] = hd1[u];
hd1[u] = tot1;
return;
}
void Tarjan(int x)
{
low[x] = dfn[x] = ++ ind;
que[ ++ top] = x;
vis[x] = 1;
for (register int i = hd[x]; i; i = nxt[i])
{
int y = to[i];
if (!dfn[y])
{
Tarjan(y);
low[x] = min(low[x], low[y]);
}
else if (vis[y]) low[x] = min(low[x], dfn[y]);
}
if (low[x] == dfn[x])
{
cnt ++ ;
int now = -1;
do
{
now = que[top -- ];
vis[now] = 0;
col[now] = cnt;
ok[cnt] |= bar[now];
sum[cnt] += money[now];
} while (now != x);
}
return;
}
void topo(int s)
{
for (register int i = 1; i <= cnt; ++ i)
dis[i] = -1e9;
dis[s] = 0;
for (register int i = 1; i <= cnt; ++ i)
if (!rd[i])
q.push(i);
while (!q.empty())
{
int x = q.front(); q.pop();
for (int i = hd1[x]; i; i = nxt1[i])
{
int y = to1[i], z = sum[y];
dis[y] = max(dis[y], dis[x] + z);
rd[y] -- ;
if (rd[y] == 0) q.push(y);
}
}
for (register int i = 1; i <= cnt; ++ i)
if (dis[i] != -1e9)
dis[i] += sum[s];
return;
}
int main()
{
n = read(); m = read();
for (register int i = 1; i <= m; ++ i)
{
int u = read(), v = read();
add(u, v);
}
for (register int i = 1; i <= n; ++ i)
money[i] = read();
s = read(); p = read();
for (register int i = 1; i <= p; ++ i)
bar[read()] = 1;
for (register int i = 1; i <= n; ++ i)
if (!dfn[i])
Tarjan(i);
for (register int x = 1; x <= n; ++ x)
{
for (register int i = hd[x]; i; i = nxt[i])
{
int y = to[i];
if (col[x] != col[y])
{
add1(col[x], col[y]);
rd[col[y]] ++ ;
}
}
}
topo(col[s]);
for (register int i = 1; i <= cnt; ++ i)
if (ok[i] && dis[i] != -1e9)
res = max(res, dis[i]);
printf("%d\n", res);
return 0;
}