祎隋

NOI2014 魔法森林
链接Solve:那么先按照A权值排序,动态维护一个最小生成树,那么加入第i条边后的最小代价就是$A_i$加上1-n...
扫描右侧二维码阅读全文
02
2019/04

NOI2014 魔法森林

链接

Solve:

那么先按照A权值排序,动态维护一个最小生成树,那么加入第i条边后的最小代价就是$A_i$加上1-n的最大值,那么这个可以通过lct完成

考虑会出现环,那么在出现环时就考虑到题目要求最小,那么我们就删掉u-v上权值最大的一条边,并连接上当前边。

对于联通性我们用并查集维护,注意这里lct和并查集分别实现,不要混淆。在lct中有一个技巧就是用upload维护最大权值的编号,这样会方便许多

注意这里要将边权化为点权,然后注意下实现细节和技巧就好。

#include<bits/stdc++.h>
using namespace std;
#define maxn 200005
int n,m,fa[maxn],lc[maxn],rc[maxn],rev[maxn],q[maxn],len,val[maxn],sm[maxn],f[maxn];
struct eage{
    int a,b,x,y;
}ask[maxn];
int cx(int x){//并查集find f和fa不一样 
    if(f[x]!=x) f[x]=cx(f[x]);
    return f[x];
}
void zm(int x,int y){//link
    int ix=cx(x),iy=cx(y);
    if(ix!=iy) f[iy]=ix;
} 
bool comp(eage a,eage b){return a.y<b.y;}
int which(int x){
    return rc[fa[x]]==x;
}
bool is_root(int x){
    return !fa[x]||(lc[fa[x]]!=x&&rc[fa[x]]!=x);
}
void upt(int x){//维护当前splay的val最大值的编号 
    sm[x]=x;//先默认为本身 
    if(lc[x]&&val[sm[lc[x]]]>val[sm[x]]) sm[x]=sm[lc[x]]; 
    if(rc[x]&&val[sm[rc[x]]]>val[sm[x]]) sm[x]=sm[rc[x]];
}
void down(int x){
    if(rev[x]){
        swap(lc[x],rc[x]);
        if(lc[x]) rev[lc[x]]^=1;
        if(rc[x]) rev[rc[x]]^=1;
        rev[x]=0;
    }
}
void rotate(int x){
    int y = fa[x], z = fa[y], b = lc[y] == x ? rc[x] : lc[x];
    if (z && !is_root(y)) (lc[z] == y ? lc[z] : rc[z]) = x;
    fa[x] = z; fa[y] = x; b ? fa[b] = y : 0;
    if (lc[y] == x) rc[x] = y, lc[y] = b;
    else lc[x] = y, rc[y] = b; upt(y); upt(x);
}
void splay(int x) {
    int i, y; q[len = 1] = x;
    for (y = x; !is_root(y); y = fa[y]) q[++len] = fa[y];
    for (i = len; i >= 1; i--) down(q[i]);
    while (!is_root(x)) {
        if (!is_root(fa[x])) {
            if (which(x) == which(fa[x])) rotate(fa[x]);
            else rotate(x);
        }
        rotate(x);
    }
    upt(x);
}
void Access(int x) {
    int y;
    for (y = 0; x; y = x, x = fa[x]) {
        splay(x); rc[x] = y;
        if (y) fa[y] = x; upt(x);
    }
}
int Find_Root(int x) {
    Access(x); splay(x);
    while (down(x), lc[x]) x = lc[x];
    splay(x); return x;
}
void Make_Root(int x) {
    Access(x); splay(x);
    rev[x] ^= 1;
}
void Link(int x, int y) {
    Make_Root(x); fa[x] = y;
}
void Cut(int x, int y) {
    Make_Root(x); Access(y); splay(y);
    lc[y] = 0; fa[x] = 0; upt(y);
}
int Select(int x, int y) {
    Make_Root(x); Access(y); splay(y);
    return sm[y];
}
int main(){
    int ans=2e9;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d%d",&ask[i].a,&ask[i].b,&ask[i].x,&ask[i].y);
    }
    sort(ask+1,ask+m+1,comp);
    for(int i=1;i<=n+m;i++) f[i]=sm[i]=i;//化边为点 
    for(int i=n+1;i<=n+m;i++) val[i]=ask[i-n].x;//边权改为点权 
    for(int i=1;i<=m;i++){//动态维护最小生成树 
        int u=ask[i].a,v=ask[i].b;bool flag=1;
        if(cx(u)==cx(v)){
            int w=Select(u,v);//返回u,v这条路径上最大权值的编号 
            if(val[w]>ask[i].x)
             Cut(ask[w-n].a,w),Cut(w,ask[w-n].b);//断边
            else flag=0; 
        }
        else zm(u,v);//并查集联通 
        if(flag) Link(u,i+n),Link(i+n,v);//lct联通
        if(cx(1)==cx(n))
         ans=min(ans,ask[i].y+val[Select(1,n)]);//因为最小生成树的缘故,妙a 
    }
    if(ans<2e9) printf("%d\n",ans);
    else printf("-1\n"); 
    return 0;
}
Last modification:April 3rd, 2019 at 10:51 am

2 comments

  1. moozik

    字体看起来有一点不习惯
    挺漂亮的blog

    1. yisui
      @moozik

      谢谢OωO(字体的大小好像改不了,handsome新版本好像会修复qwq

Leave a Comment