题解 P4180 【【模板】严格次小生成树[BJWC2010]】
被这题的INF值给弄自闭了
很气
题目链接:P4180 【模板】严格次小生成树[BJWC2010]
看了 只是我不会 ,但是我的做法是 Kruskal + 树剖,这个做法的好像只有一位巨佬写了题解 然而我没看懂 ,所以自己来写一篇
至于为什么写树剖。。。是某
首先强烈申明,此题的
我这边开的是
进入正题
首先可以证明:至少有一个严格次小生成树和最小生成树只有一条边的差异 只是我不会证
所以首先就可以先跑
听着很简单对吧
但是此处这个更新的过程就很有意思了。当我们现在枚举一条边
我们把
删哪一条呢?
请注意标题:严格次小生成树,那么我们需要找到这条路径上边权最大的那个点,删除它,这样才能保证新的生成树与
再注意两个字:严格,也就是说,新的生成树和
所以,我们不仅要维护两点之间的最大值,还要维护两点之间的严格次大值
那树剖的算法就很明朗啦,先跑
以下代码会先分为几块来讲解
首先是代码的变量申明
#define pii pair<long long,long long>
#define INF 2147483647000000
const long long MAXN = 4e5 + 10;
const long long MAXM = 3e5 + 10;
long long n,m,mst,smst;
//mst 最小生成树的边权和,smst 严格次小生成树的边权和
long long ufs[MAXN],nodecnt,ww[MAXN];
//ufs 并查集 nodecnt,点数(之后会加),ww点权
long long head[MAXN],edgesize;
//建图用
long long dep[MAXN],son[MAXN],fa[MAXN],siz[MAXN];
long long id[MAXN],top[MAXN],w[MAXN],nodesize;
//树剖套餐
和三个结构体
struct EDGE_For_Kru {
long long x,y,w;
bool select;
bool operator < (const EDGE_For_Kru &b) const {return w < b.w;}
} G[MAXM << 1];//最小生成树用的
struct EDGE {
long long nxt;
long long to;
} edge[MAXM << 2];//树剖用的
struct SegmentTree {
long long val1;//最大值
long long val2;//严格次大值
} seg[MAXN << 2];//线段树
最水的部分
就是简单的跑了,没什么好说的。
long long find(long long x) {
if(ufs[x] != x) ufs[x] = find(ufs[x]);
return ufs[x];
}
void unity(long long x,long long y) {
if(find(x) != find(y))
ufs[find(y)] = find(x);
}
inline long long Kruskal() {
nodecnt = n;//首先把点的个数设为 n
sort(G + 1,G + m + 1);
long long cnt = 0,res = 0;
for(long long i = 1;i <= n; ++i)
ufs[i] = i;
for(long long i = 1;i <= m; ++i) {
long long x = G[i].x,y = G[i].y;
if(find(x) == find(y)) continue;
unity(x,y); G[i].select = true;
addedge(x,++nodecnt); addedge(nodecnt,x);//加入树剖的边
addedge(y,nodecnt); addedge(nodecnt,y);//同上
ww[nodecnt] = G[i].w;//新点的权值就是边权
cnt++, res += G[i].w;
if(cnt == n - 1) break;
}
return res;
}
接着就是树剖部分啦
其实大部分就是树剖常规打法,两个DFS直接就是裸的,只不过线段树方面稍微要注意,在更新两个
以下的
void DFS1(long long now,long long f,long long deep) {
dep[now] = deep;
fa[now] = f;
siz[now] = 1;
long long maxson = -1;
for(long long i = head[now];i;i = edge[i].nxt) {
if(edge[i].to == f) continue;
DFS1(edge[i].to,now,deep + 1);
siz[now] += siz[edge[i].to];
if(siz[edge[i].to] > maxson) {
maxson = siz[edge[i].to];
son[now] = edge[i].to;
}
}
}//最平凡的DFS1
void DFS2(long long now,long long topf) {
id[now] = ++nodesize;
top[now] = topf;
w[nodesize] = ww[now];
if(!son[now]) return;
DFS2(son[now],topf);
for(long long i = head[now];i;i = edge[i].nxt) {
if(edge[i].to == son[now] || edge[i].to == fa[now]) continue;
DFS2(edge[i].to,edge[i].to);
}
}//最平凡的DFS2
pii getval(pii v1,pii v2) {//从下面四个值中间找出最大值和严格次大值
long long res1,res2,t = 3;
long long v11 = v1.first,v12 = v1.second;
long long v21 = v2.first,v22 = v2.second;
long long vv[5] = {0,v11,v12,v21,v22};
sort(vv + 1,vv + 5);//给这四个值排序
res1 = vv[4];//最大值
while(vv[t] == vv[4]) t--;//严格次大值
res2 = vv[t];
return make_pair(res1,res2);//返回pii
}
void build(long long root,long long l,long long r) {
if(l == r) {
seg[root].val1 = w[l];
seg[root].val2 = 0;
return;
}
long long mid = (l + r) >> 1;
build(root << 1,l,mid);
build(root << 1 | 1,mid + 1,r);
pii q = make_pair(seg[root << 1].val1,seg[root << 1].val2);
pii p = make_pair(seg[root << 1 | 1].val1,seg[root << 1 | 1].val2);//两棵子树的分别两个值
pii res = getval(p,q); //更新本节点的值
seg[root].val1 = res.first,seg[root].val2 = res.second;
}
pii query(long long root,long long l,long long r,long long L,long long R) {
if(L <= l && r <= R) return make_pair(seg[root].val1,seg[root].val2);
long long mid = (l + r) >> 1;
pii res;
if(L <= mid) res = getval(res,query(root << 1,l,mid,L,R));
if(R > mid) res = getval(res,query(root << 1 | 1,mid + 1,r,L,R));
//同样是更新
return res;
}
pii QUERY(long long x,long long y) {
pii res;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x,y);
res = getval(res,query(1,1,nodesize,id[top[x]],id[x]));//也是一样更新
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x,y);
pii g = query(1,1,nodesize,id[x],id[y]);
res = getval(res,g);
return res;
}
所有
以上代码建议仔细阅读,这是整个代码中最关键部分的实现
再说主函数
其实就是一些平常的输入啊之类的,接着就是上方代码思路描述部分的代码实现了
read(n); read(m);//自己写的龟速读
for(long long i = 1;i <= m; ++i) {
read(G[i].x);
read(G[i].y);
read(G[i].w);//输入Kruskal的边
}
mst = Kruskal();//mst的边权和
smst = INF;//次小生成树的边权和,初始化成 INF
DFS1(1,0,1); DFS2(1,1);//树剖
build(1,1,nodesize);//树剖
for(long long i = 1;i <= m; ++i) {
if(G[i].select) continue;//如果这条边在mst中,就不能选
long long wn = G[i].w;//加入的新边的边权
pii tmp = QUERY(G[i].x,G[i].y);//树剖找出两点之间的最大和严格次大边权
if(tmp.first != wn) smst = min(smst,mst - tmp.first + wn);
//如果最大值和新边权不相等,就用最大值直接更新
else smst = min(smst,mst - tmp.second + wn);
//否则说明如果去掉最大值再加入新边权后,边权和将不会变化,不满足"严格次小"的规定,则需要用严格次大值更新
}
cout << smst << endl;//输出即可
最后的代码(蒟蒻不知道为什么,这个代码不开那就开着吧)
//Code By CXY
#include<bits/stdc++.h>
using namespace std;
#define pii pair<long long,long long>
#define INF 2147483647000000
const long long MAXN = 4e5 + 10;
const long long MAXM = 3e5 + 10;
struct EDGE_For_Kru {
long long x,y,w;
bool select;
bool operator < (const EDGE_For_Kru &b) const {return w < b.w;}
} G[MAXM << 1];
struct EDGE {
long long nxt;
long long to;
} edge[MAXM << 2];
struct SegmentTree {
long long val1;
long long val2;
} seg[MAXN << 2];
long long n,m,mst,smst;
long long ufs[MAXN],nodecnt,ww[MAXN];
long long head[MAXN],edgesize;
long long dep[MAXN],son[MAXN],fa[MAXN],siz[MAXN];
long long id[MAXN],top[MAXN],w[MAXN],nodesize;
template <typename T> inline void read(T &a) {
a = 0;char c = getchar();long long f = 1;
while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
while(c >= '0' && c <= '9') {a = (a << 3) + (a << 1) + (c ^ 48);c = getchar();}
a *= f;
}
inline void addedge(long long x,long long y) {
edge[++edgesize].nxt = head[x];
edge[edgesize].to = y;
head[x] = edgesize;
}
//-----------------------------------------------------------------------
long long find(long long x) {
if(ufs[x] != x) ufs[x] = find(ufs[x]);
return ufs[x];
}
void unity(long long x,long long y) {
if(find(x) != find(y))
ufs[find(y)] = find(x);
}
inline long long Kruskal() {
nodecnt = n;
sort(G + 1,G + m + 1);
long long cnt = 0,res = 0;
for(long long i = 1;i <= n; ++i)
ufs[i] = i;
for(long long i = 1;i <= m; ++i) {
long long x = G[i].x,y = G[i].y;
if(find(x) == find(y)) continue;
unity(x,y); G[i].select = true;
addedge(x,++nodecnt); addedge(nodecnt,x);
addedge(y,nodecnt); addedge(nodecnt,y);
ww[nodecnt] = G[i].w;
cnt++, res += G[i].w;
if(cnt == n - 1) break;
}
return res;
}
//-----------------------------------------------------------------------
void DFS1(long long now,long long f,long long deep) {
dep[now] = deep;
fa[now] = f;
siz[now] = 1;
long long maxson = -1;
for(long long i = head[now];i;i = edge[i].nxt) {
if(edge[i].to == f) continue;
DFS1(edge[i].to,now,deep + 1);
siz[now] += siz[edge[i].to];
if(siz[edge[i].to] > maxson) {
maxson = siz[edge[i].to];
son[now] = edge[i].to;
}
}
}
void DFS2(long long now,long long topf) {
id[now] = ++nodesize;
top[now] = topf;
w[nodesize] = ww[now];
if(!son[now]) return;
DFS2(son[now],topf);
for(long long i = head[now];i;i = edge[i].nxt) {
if(edge[i].to == son[now] || edge[i].to == fa[now]) continue;
DFS2(edge[i].to,edge[i].to);
}
}
pii getval(pii v1,pii v2) {
long long res1,res2,t = 3;
long long v11 = v1.first,v12 = v1.second;
long long v21 = v2.first,v22 = v2.second;
long long vv[5] = {0,v11,v12,v21,v22};
sort(vv + 1,vv + 5);
res1 = vv[4];
while(vv[t] == vv[4]) t--;
res2 = vv[t];
return make_pair(res1,res2);
}
void build(long long root,long long l,long long r) {
if(l == r) {
seg[root].val1 = w[l];
seg[root].val2 = 0;
return;
}
long long mid = (l + r) >> 1;
build(root << 1,l,mid);
build(root << 1 | 1,mid + 1,r);
pii q = make_pair(seg[root << 1].val1,seg[root << 1].val2);
pii p = make_pair(seg[root << 1 | 1].val1,seg[root << 1 | 1].val2);
pii res = getval(p,q); seg[root].val1 = res.first,seg[root].val2 = res.second;
}
pii query(long long root,long long l,long long r,long long L,long long R) {
if(L <= l && r <= R) return make_pair(seg[root].val1,seg[root].val2);
long long mid = (l + r) >> 1;
pii res;
if(L <= mid) res = getval(res,query(root << 1,l,mid,L,R));
if(R > mid) res = getval(res,query(root << 1 | 1,mid + 1,r,L,R));
return res;
}
pii QUERY(long long x,long long y) {
pii res;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x,y);
res = getval(res,query(1,1,nodesize,id[top[x]],id[x]));
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x,y);
pii g = query(1,1,nodesize,id[x],id[y]);
res = getval(res,g);
return res;
}
//-----------------------------------------------------------------------
int main () {
read(n); read(m);
for(long long i = 1;i <= m; ++i) {
read(G[i].x);
read(G[i].y);
read(G[i].w);
}
mst = Kruskal();
smst = INF;
DFS1(1,0,1); DFS2(1,1);
build(1,1,nodesize);
for(long long i = 1;i <= m; ++i) {
if(G[i].select) continue;
long long wn = G[i].w;
pii tmp = QUERY(G[i].x,G[i].y);
if(tmp.first != wn) smst = min(smst,mst - tmp.first + wn);
else smst = min(smst,mst - tmp.second + wn);
}
cout << smst << endl;
return 0;//华丽丽地结束
}
到这里就结束了,本代码中最有趣的部分应该就是那个 虽然把我调到自闭,大家可以 自行品味 多多研读
初二的