题目大意:
Q
Y
Q
QYQ
QYQ有
K
K
K次技能,每次可以从一个岛屿上闪现到另外一个岛屿上,每一个岛屿只能登上一次。
Q
Y
Q
QYQ
QYQ能从任何一个城市开始旅程,也能在任何一个城市结束旅程。
城市的数量有
n
n
n个,有
m
m
m条有向边,两个城市属于相同的岛屿当且仅当暂时将所有道路视为双向道路时可以从其中一个城市走到另一个城市(可以途径其它城市)。
每一个城市都有一个宝物,价值
v
i
v_i
vi,
问得到最大总价值的宝物之和。
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
1<=n<=100000,1<=m<=1000000,1<=v[i]<=1000,0<=K<=100000
图中可能会有重边、自环。
分析:
排序去自环重边,
t
a
r
j
a
n
tarjan
tarjan求强连通分量并缩点,转变成有向无环图,
然后按拓扑序动规,
f_i表示以点
i
i
i 结束,最多可以获得的总价值。
(
i
为
缩
点
后
的
点
,
即
一
个
强
连
通
分
量
)
(i为缩点后的点,即一个强连通分量)
(i为缩点后的点,即一个强连通分量)
则
f
i
=
m
a
x
(
f
[
所
有
能
到
i
的
点
]
)
+
v
i
f_i=max(f[所有能到 i 的点])+v_i
fi=max(f[所有能到i的点])+vi
然后对应的岛屿
j
j
j要看能不能更新收获最大值
m
a
x
f
j
maxf_j
maxfj
里面所有岛屿的收获的前
k
+
1
k+1
k+1大之和即为答案
对于如何求一个点对应的岛屿呢,并查集即可,然后一个强连通分量中的所有点必定在同一个岛屿,所以缩点后对应的岛屿不变。
时间复杂度 O
(
m
+
n
l
o
g
n
)
(m+nlogn)
(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;
}