jzoj5643【NOI2018模拟4.10】最小代价

题目

给你一个n个点m条边无向图,每一个点有两个权a,b,现在要你选出恰好k个点,其中这k个点可以形成恰好1个联通块,对于每一个这样的选取方案,代价是max{a}+max{b},问最小的代价
其中n<=3e5,m<=5e5

题解

大概的思路就是把每一条边都赋一个边权,把问题转化成选k-1条边,然后就可以lct(快速的求子树的大小)了

需要注意的是lct中不应该连的边不要乱连,没有的边不要乱删,就是因为这个东西调了超级久
以后做lct WA时应该要好好注意这两个东西

贴代码

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fo1(i,b,a) for(i=b;i>=a;i--)
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))

using namespace std;

const int maxn=5e5+5;

struct P{
    int x,y,t1,t2,id;
}a[maxn],s[maxn],q[maxn],g[maxn];
int son[maxn*2][3],fa[maxn*2],st[maxn*2],size[maxn*2],mi[maxn*2];
int xu[maxn*2];
int i,j,k,l,m,n,x,y,z,nt,la,ans,tp;
bool bz[maxn*2],bt[maxn*2],hv[maxn*2];

int cmp1(P x,P y){
    return x.t1<y.t1;
}
int cmp2(P x,P y){
    return x.t2<y.t2;
}
int read(){
    int x=0; char ch=getchar();
    while (ch<'0' || ch>'9') ch=getchar();
    while (ch>='0' && ch<='9'){
        x=x*10+ch-48;
        ch=getchar();
    }
    return x;
}

//LCT
bool isroot(int x){
    if (son[fa[x]][0]==x || son[fa[x]][1]==x) return false;
    return true;
}
void remark(int x){
    if (bz[x]==true){
        bz[x]=false;
        int z=son[x][0]; son[x][0]=son[x][1]; son[x][1]=z;
        bz[son[x][0]]^=1; bz[son[x][1]]^=1;
    }
}
int update(int x){
    size[x]=size[son[x][0]]+size[son[x][1]]+xu[x];
    if (x>n){
        if (g[mi[son[x][0]]].t2>g[x-n].t2) mi[x]=mi[son[x][0]]; else
            mi[x]=x-n;
    } else mi[x]=mi[son[x][0]];
    if (g[mi[son[x][1]]].t2>g[mi[x]].t2) mi[x]=mi[son[x][1]];
}
void rotate(int x,int w){
    int y=fa[x];
    son[y][2-w]=son[x][w-1];
    if (son[x][w-1]) fa[son[x][w-1]]=y;
    fa[x]=fa[y];
    if (!isroot(y)){
        if (y==son[fa[y]][0]) son[fa[y]][0]=x; else son[fa[y]][1]=x;
    }
    fa[y]=x;
    son[x][w-1]=y;
    update(y); update(x);
}
void splay(int x){
    int i,y;
    tp=0;
    while (!isroot(x)){
        st[++tp]=x; x=fa[x];
    }
    st[++tp]=x;
    fo1(i,tp,1) remark(st[i]);
    x=st[1];
    while (!isroot(x)){
        y=fa[x];
        if (isroot(y)){
            if (x==son[y][0]) rotate(x,2); else rotate(x,1);
        } else{
            if (y==son[fa[y]][0]){
                if (x==son[y][0]){
                    rotate(y,2); rotate(x,2);
                } else{
                    rotate(x,1); rotate(x,2);
                }
            } else{
                if (x==son[y][1]){
                    rotate(y,1); rotate(x,1);
                } else{
                    rotate(x,2); rotate(x,1);
                }
            }
        }
    }
}
void access(int x){
    for(int t=0;x;x=fa[x]){
        splay(x); xu[x]=xu[x]+size[son[x][1]]-size[t];
        son[x][1]=t; update(x); t=x;
    }
}
void makeroot(int x){
    access(x);
    splay(x); bz[x]^=1;
}
void link(int x,int y){
    makeroot(x);// makeroot(y);
    fa[x]=y; xu[y]=xu[y]+size[x]; update(y);
}
void cut(int x,int y){
    makeroot(x); access(y); splay(y); son[y][0]=0; fa[x]=0;
    update(y);
}
bool tong(int x,int y){
    makeroot(x); access(y); splay(y);
    if (isroot(x)) return false; return true;
}
bool pan(int x){
    makeroot(x);
    if (size[x]return false; return true;
}
void Link(int x){
    if (pan(g[x].x)) nt--;
    if (pan(g[x].y)) nt--;
    link(g[x].x,x+n);
    link(g[x].y,x+n);
    if (pan(x+n)) nt++;
}
void Cut(int x){
    if (pan(x+n)) nt--;
    cut(g[x].x,x+n);
    cut(g[x].y,x+n);
    if (pan(g[x].x)) nt++;
    if (pan(g[x].y)) nt++;
}
int find(int x,int y){
    makeroot(x); access(y); splay(y);
    return mi[y];
}
//
void insert(int i){
    bt[s[i].id]=true;
    if (hv[s[i].id]) return;
    if (tong(s[i].x,s[i].y)){
        z=find(s[i].x,s[i].y);
        if (s[i].t2s[i].id);
        } else bt[s[i].id]=false;
    } else
    Link(s[i].id);
}
void go_del(){
    while (true){
        if (bt[q[la].id]==false){
            hv[q[la].id]=true; la--; continue;
        }
        Cut(q[la].id);
        if (!nt){
            Link(q[la].id); break;
        }
        hv[q[la].id]=true;
        la--;
    }
    ans=min(ans,s[i].t1+q[la].t2);
}
void prepare(){
    fo(i,1,m){
        xu[i+n]=1; mi[i+n]=i;
    }
    i=0;
    while (nt==0 && i<m){
        i++;
        insert(i);
    }
    if (nt==0){
        printf("no solution"); return;
    }
    la=m;
    go_del();
    i++;
}
int main(){
    freopen("mincost.in","r",stdin);
    freopen("mincost.out","w",stdout);
    n=read(); m=read(); k=read();
    fo(i,1,n){
        a[i].x=read(); a[i].y=read(); a[i].id=i;
    }
    if (k==1){
        ans=2e9;
        fo(i,1,n) ans=min(ans,a[i].x+a[i].y);
        printf("%d\n",ans);
        return 0;
    }
    k--;
    fo(i,1,m){
        s[i].x=read(); s[i].y=read(); s[i].id=i;
        s[i].t1=max(a[s[i].x].x,a[s[i].y].x);
        s[i].t2=max(a[s[i].x].y,a[s[i].y].y);
        g[i]=s[i];
        q[i]=s[i];
    }
    sort(s+1,s+m+1,cmp1);
    sort(q+1,q+m+1,cmp2);
    ans=2e9;
    prepare();
    if (!nt) return 0;
    for(;i<=m;i++){
        insert(i);
        go_del();
    }
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(lct)