整个笔记注意力惊人,慎入......
持续更新。
P2700 逐个击破
能卡住我的黄题已经很少见了,但这道题确实又是一个。唉,只能说自己依然是蒟蒻吧。
不过,由于题目很容易理解,加上自己因为刷难题身心俱疲,“玩”一下这种简单的题目也算是种放松。
不能因为刷题,把自己学算法的乐趣搞没了。
先上个TLE代码,也就排序那一步和kruskal有点关系。
从权重最小的边开始,判断它所连两结点是否与标记结点相连,如果是,则删去。
但它确实是我自己发明的做法。
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstring>
#define int unsigned long long
using namespace std;
int n, m, k;
int mark[100005] = { 0 };
int cut[100005] = { 0 };
int vis[100005] = { 0 };
struct Edge
{
int u, v, w, id;
};
vector<Edge> e[100005];
vector<Edge> Alledge;
bool cmp(Edge& a, Edge& b) {
return a.w < b.w;
}
bool islink(int top) {
if (mark[top]) return true;
for (auto [u, v, w, id] : e[top]) {
if (vis[id]) continue;
vis[id] = 1;
bool res = islink(v);
vis[id] = 0;
if (res) return true;
}
return false;
}
signed main()
{
cin >> n >> k;
int u, v, w;
int ans = 0;
int x;
for (int i = 1; i <= k; i++) {
cin >> x;
mark[x] = 1;
}
for (int i = 1; i <= n - 1; i++) {
cin >> u >> v >> w;
e[u].push_back({ u,v,w,i });
e[v].push_back({ v,u,w,i });
Alledge.push_back({ u,v,w,i });
}
sort(Alledge.begin(), Alledge.end(), cmp);
for (auto [u, v, w, id] : Alledge) {
vis[id] = 1;
if (islink(u) and islink(v)) ans += w, k--;
else vis[id] = 0;
if (!k) break;
}
cout << ans;
return 0;
}
好的,接下来我们开始卡常。
可以知道开销主要就是在判断是否与标记结点连通这一步。
注意到连接两个标记结点的边必须断开,所以可以先预处理一下:
for (int i = 1; i <= n - 1; i++) {
cin >> u >> v >> w;
if (mark[u] and mark[v]) {
ans += w;
continue;
}
e[u].push_back({ u,v,w,i });
e[v].push_back({ v,u,w,i });
Alledge.push_back({ u,v,w,i });
}
还是超时。
注意到:
for (auto [u, v, w, id] : Alledge) {
vis[id] = 1;
if (islink(u) and islink(v)) ans += w, k--;
else vis[id] = 0;
if (!k) break;
}
else vis[id] = 0是不需要的。读者应该不难理解......形象一点说,如果这个边不连接特殊结点,那么后续也就不必通过此边来寻找特殊结点。
还是超时......
注意到有些边是不用进行判断的:如果它与前一个判断的边直接相连,如果前一条边断开了,那么它就一定不连接标记结点的边。
bool islink(int top) {
if (mark[top]) return true;
for (auto [u, v, w, id] : e[top]) {
if (vis[id]) continue;
vis[id] = 1;
bool res = islink(v);
if (res) {
vis[id] = 0;
return true;
}
}
return false;
}
居然过了......
那么,这和kruskal算法的关系是什么......
注意到可以先把所有边除掉,然后去连可以连的边,把被标记点视作一个连通域。kruskal算法是不会在连通域间相连的。
连边时,需要先连代价最大的......
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstring>
#define int unsigned long long
using namespace std;
int n, m, k;
int rt[100005] = { 0 };
struct Edge
{
int u, v, w, id;
};
vector<Edge> Alledge;
bool cmp(Edge& a, Edge& b) {
return a.w > b.w;
}
int findroot(int x) {
return x == rt[x] ? x : rt[x] = findroot(rt[x]);
}
void un(int x, int y) {
rt[findroot(x)] = findroot(y);
}
signed main()
{
cin >> n >> k;
int u, v, w;
int ans = 0;
int x;
int pre = 0;
for (int i = 1; i <= n; i++) rt[i] = i;
for (int i = 1; i <= k; i++) {
cin >> x;
if (pre) {
rt[x] = pre;
}
else pre = x;
}
for (int i = 1; i <= n - 1; i++) {
cin >> u >> v >> w;
ans += w;
Alledge.push_back({ u,v,w,i });
}
sort(Alledge.begin(), Alledge.end(), cmp);
for (auto [u, v, w, id] : Alledge) {
int xr = findroot(u), yr = findroot(v);
if (xr != yr) {
un(xr, yr);
ans -= w;
}
}
cout << ans;
return 0;
}
标签:洛谷,OI,int,Alledge,vis,return,include,id,刷题
From: https://www.cnblogs.com/tomorin/p/18686978