专栏文章
题解:P5008 [yLOI2018] 锦鲤抄
P5008题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @min4en56
- 此快照首次捕获于
- 2025/12/01 20:23 3 个月前
- 此快照最后确认于
- 2025/12/01 20:23 3 个月前
题意
题意简化版说的很清楚了。
分析
我们先贪心的思考,在选点时一定是优先选权值大的。然后我们再考虑哪些点能选。
先把图缩点变成一个 DAG 然后,我们发现所有入度不为 的强连通分量的所有点都是可选点,反之则其中必须有一个点作为火源,也就是必须留一个点不选,所以尽量贪权值最小的不选。
看数据范围时,我们发现题目不保证没有自环,还是个善良题面,于是我们思考自环会导致什么。容易发现,如果一个强连通分量中不选的点有自环,则其可以被选。
综上,把满足条件的点挑出来,排个序,取前 个,累加然后输出,即可切掉本题。
CPP#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5 + 10, K = 20, mod = 1e9 + 7;
int n, m, f[N], dfn[N], ar, a[N], low[N], ins[N], scc[N], col, cnti[N], en[N];
vector <int> edge[N], blg[N];
stack <int> st;
int chkmn(int &a, const int &b) {return a = min(a, b);}
void tarjan(int p)
{
dfn[p] = low[p] = ++ ar;
st.push(p); ins[p] = 1;
for (auto to : edge[p])
{
if (!dfn[to])
{
tarjan(to);
chkmn(low[p], low[to]);
}
else if (ins[to])
chkmn(low[p], dfn[to]);
}
if (low[p] == dfn[p])
{
col ++;
while (!st.empty())
{
int t = st.top(); st.pop();
ins[t] = 0;
scc[t] = col;
blg[col].emplace_back(t);
if (t == p)
break;
}
}
}
signed main() {
cin.tie(0)->sync_with_stdio(0);
int k;
cin >> n >> m >> k;
for (int i = 1; i <= n; i ++) cin >> a[i];
while (m --)
{
int u, v;
cin >> u >> v;
edge[u].push_back(v);
}
for (int i = 1; i <= n; i ++) if (!dfn[i]) tarjan(i);
for (int i = 1; i <= n; i ++){
for (auto to : edge[i])
{
if (scc[to] != scc[i]) cnti[scc[to]] ++;
if (to == i) en[scc[i]] = 1;
}
}
auto cmp = [](int x, int y){return a[x] > a[y];};
vector <int> p;
for (int i = 1; i <= col; i ++)
{
if (en[i] || cnti[i]) for (auto it : blg[i]) p.emplace_back(a[it]);
else
{
sort(blg[i].begin(), blg[i].end(), cmp);
for (int j = 0; j < blg[i].size() - 1; j ++) p.emplace_back(a[blg[i][j]]);
}
}
int ans = 0;
if (k > p.size()) k = p.size();
nth_element(p.begin(), p.begin() + k, p.end(), greater<int>());
for (int i = 0; i < k; i ++) ans += p[i];//;, cout << p[i] << " ";
// cout << endl;
cout << ans << endl;
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...