【JZOJ5433】图

Description

有一个n个点A+B条边的无向连通图,有一变量x,每条边的权值都是一个关于x的简单多项式,其中有A条边的权值是k+x,另外B条边的权值是k-x,如果只保留权值形如k+x的边,那么这个图仍是一个连通图,如果只保留权值形如k-x的边,这个图也依然是一个连通图。
给出q组询问,每组询问给出x的值,问此时这个无向连通图的最小生成树权值是多少。

Solution

有一个肯(xian)定(ran)的结论:无论 x 的值是多少,最后的最小生成树的边一定是k+x边构成的图中的最小生成树上的边或k-x边构成的图中的最小生成树上的边。

于是,先把k+x图建出来最小生成树,将k-x中最小生成树的边拿出来排序,从小到大加进k+x图,每次把形成新环上最大的k+x边删掉,同时算出这条k-x边替代环上最大k+x边的下界,然后把这些下界排序,同时把询问挂上去查询即可。

这里可能加入的两条k-x边之间有交,但实际上没有关系,k小的那条边会先做出贡献。

删边加边可以用LCT维护。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 100010
#define ll long long
#define inf 2147483647
using namespace std;
struct node{
    int u,v;
    ll w;
}a[N*2],b[N*2];
int fa[N*5],son[N*5][2],mx[N*5],p[N*5],sz[N*5],rev[N*5];
ll va[N*5];
int pd(int x){
    return x==son[fa[x]][1];
}
void update(int x){
    int l=son[x][0],r=son[x][1];
    mx[x]=x;
    if(va[mx[l]]>va[mx[x]]) mx[x]=mx[l];
    if(va[mx[r]]>va[mx[x]]) mx[x]=mx[r];
    sz[x]=1+sz[l]+sz[r];
}
void put(int x){
    if(!rev[x]) return;
    swap(son[x][0],son[x][1]);
    rev[son[x][0]]^=1,rev[son[x][1]]^=1;
    rev[x]=0;
}
int d[N];
void down(int x,int y){
    d[0]=0;
    while(x!=y) d[++d[0]]=x,x=fa[x];
    fd(i,d[0],1) put(d[i]);
}
void rotate(int x){
    int y=fa[x],t=pd(x);
    son[y][t]=son[x][1-t];
    if(son[x][1-t]) fa[son[x][1-t]]=y;
    fa[x]=fa[y];
    if(fa[y]) son[fa[y]][pd(y)]=x;
    else p[x]=p[y],p[y]=0;
    son[x][1-t]=y,fa[y]=x;
    update(y),update(x);
}
void splay(int x,int t){
    down(x,t);
    while(fa[x]!=t)
    {
        int y=fa[x];
        if(fa[y]!=t)
            if(pd(y)==pd(x)) rotate(y);
            else rotate(x);
        rotate(x);
    }
}
void access(int x){
    int y=0;
    while(x)
    {
        splay(x,0);
        fa[son[x][1]]=0,p[son[x][1]]=x;
        son[x][1]=y,fa[y]=x,p[y]=0;
        update(x),y=x,x=p[x];
    }
}
void makeroot(int x){
    access(x),splay(x,0),rev[x]^=1;
}
void link(int x,int y){
    makeroot(x),p[x]=y;
}
void cut(int x,int y){
    makeroot(x),access(y);
    splay(y,0),son[y][0]=fa[x]=p[x]=0;
    update(y);
}
bool check(int x,int y){
    makeroot(x),access(y);
    splay(x,0);
    while(y && y!=x) y=fa[y];
    if(!y) return false;
    return true;
}
int query(int x,int y){
    makeroot(x),access(y),splay(x,0);
    return mx[x];
}
int fat[N];
int getfa(int x){
    return fat[x]==x?fat[x]:fat[x]=getfa(fat[x]);
}
struct node2{
    int x,t;
}Q[N];
bool cmp(node x,node y){
    return x.w<y.w;
}
bool cmp2(node2 x,node2 y){
    return x.x<y.x;
}
int z[N];
ll an[N];
int tot=0;
int main()
{
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    int n,A,B,q;
    scanf("%d %d %d %d",&n,&A,&B,&q);
    fo(i,0,n) va[i]=-inf;
    fo(i,1,A)
    scanf("%d %d %lld",&a[i].u,&a[i].v,&a[i].w);
    sort(a+1,a+A+1,cmp);
    int cn=0;
    ll ans=0;
    fo(i,1,A)
    if(!check(a[i].u,a[i].v)){
        cn++,ans+=a[i].w,va[i+n]=a[i].w;
        link(a[i].u,i+n),link(i+n,a[i].v);
        if(cn==n-1) break;
    }
    fo(i,1,B) scanf("%d %d %lld",&b[i].u,&b[i].v,&b[i].w);
    sort(b+1,b+B+1,cmp);
    fo(i,1,n) fat[i]=i;
    cn=0;
    fo(i,1,B)
    {
        int fx=getfa(b[i].u),fy=getfa(b[i].v);
        if(fx!=fy)
        {
            fat[fy]=fx;
            b[++cn]=b[i];
            if(cn==n-1) break;
        }
    }
    fo(i,1,cn)
    {
        int u=b[i].u,v=b[i].v,w=b[i].w;
        int t=query(u,v);
        if(t<=n) continue;
        z[++tot]=w-va[t];
        cut(a[t-n].u,t),cut(t,a[t-n].v);
        link(u,v);
    }
    sort(z+1,z+tot+1);
    fo(i,1,q) scanf("%d",&Q[i].x),Q[i].t=i;
    sort(Q+1,Q+q+1,cmp2);
    int pos=0;
    ll tmp=0;
    fo(i,1,q)
    {
        while(pos<tot && z[pos+1]<=Q[i].x*2) pos++,tmp+=z[pos];
        an[Q[i].t]=ans+tmp+(ll)(n-1-pos)*Q[i].x-(ll)pos*Q[i].x;
    }
    fo(i,1,q) printf("%lld\n",an[i]);
}
posted @ 2017-10-31 21:26  sadstone  阅读(50)  评论(0编辑  收藏  举报