【NOIP2017提高A组集训10.28】图

Description

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

Solution

其实这是一道非常套路的题目。
首先我们很显然的要对询问离线处理,然后在x等于无穷小的时候,明显全部都是A树里面的边。然后当x逐渐变大,B树里的边就会逐渐替换掉A树里的边,但是B树的边不会换掉B树的边。
那么我们可以考虑把B树的k从小到大排序,然后逐个替换掉A树中的边,就是用LCT来维护最小生成树,然后算出这条边什么时候会替换掉它,最后把替换掉的时间排一个序,把询问里面的时间离线的搞,求出答案。
但是我们会发现根据时间来说,有可能k比较大的边在k比较小的之间替换掉树中的边,首先假如这构成的环不相交是不会影响的。
但是相交的情况怎么办?
第一种情况是虽然是相交,但是他们取得的环上最大值不在同一个环内,这样是不会影响的。
第二种情况是取得的环上最大值在同一个环类,因为不过先后,在时间线上的形态还是一样的,所以没有影响了。
然后这题就变成了经典的用LCT维护最小生成树的题目。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=6e5+7;
ll i,j,k,l,n,m,ans,A,B,q;
ll an[maxn],f[maxn],u,v,d[maxn],bz[maxn],x,y,pp;
ll t[maxn][2],fa[maxn],mx[maxn],c[maxn],p[maxn],num,pfa[maxn],inf;
struct node{
    ll x,y,k,o;
}a[maxn],b[maxn],e[maxn],s[maxn];
bool cmp(node x,node y){return x.kint gf(ll x){return(!f[x])?x:f[x]=gf(f[x]);}
int son(ll x){return t[fa[x]][0]!=x;}
void update(ll x){
    ll l=t[x][0],r=t[x][1];mx[0]=-inf;
    if(mx[l]>mx[r])mx[x]=mx[l],p[x]=p[l];
    else mx[x]=mx[r],p[x]=p[r];
    if(c[x]>mx[x])mx[x]=c[x],p[x]=x;
}
void rotate(ll x){
    ll y=fa[x],z=son(x);
    t[y][z]=t[x][1-z];fa[t[x][1-z]]=y;
    t[fa[x]=fa[y]][son(y)]=x;;
    if(pfa[y])pfa[x]=pfa[y],pfa[y]=0;
    t[fa[y]=x][1-z]=y;
    update(y),update(x);
}
ll make(ll x){
    if(bz[x]){
        swap(t[x][0],t[x][1]);
        bz[t[x][0]]^=1,bz[t[x][1]]^=1;bz[x]=0;
    }
}
void remove(ll x,ll y){
    while(x!=y)d[++d[0]]=x,x=fa[x];
    while(d[0])make(d[d[0]--]);
}
void splay(ll x,ll y){
    remove(x,y);
    while(fa[x]!=y){
        if(fa[fa[x]]!=y)if(son(fa[x])==son(x))rotate(fa[x]);else rotate(x);
        rotate(x);
    }
}
ll access(ll x){
    ll y=0;
    while(x){
        splay(x,0);
        pfa[t[x][1]]=x;fa[t[x][1]]=0;
        t[fa[y]=x][1]=y;pfa[y]=0;
        update(x);
        y=x,x=pfa[x];
    }
}
void makeroot(ll x){access(x);splay(x,0);bz[x]^=1;}
void link(ll x,ll y){makeroot(y);pfa[y]=x;}
void cut(ll x,ll y){
    makeroot(x);access(y);splay(y,0);
    t[y][0]=fa[x]=pfa[x]=0;update(y);
}
int main(){
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    scanf("%lld%lld%lld%lld",&n,&A,&B,&q);inf=9999999999999999;
    fo(i,1,A)scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].k);
    fo(i,1,B)scanf("%lld%lld%lld",&b[i].x,&b[i].y,&b[i].k);
    sort(a+1,a+1+A,cmp);sort(b+1,b+1+B,cmp);
    fo(i,1,A)c[i+n]=a[i].k;fo(i,1,B)c[n+A+i]=-inf;
    fo(i,1,n)c[i]=-inf;
    fo(i,1,A){
        u=gf(a[i].x),v=gf(a[i].y);
        if(u==v)continue;ans+=a[i].k;
        m++;f[u]=v;link(a[i].x,n+i);link(n+i,a[i].y);
        if(m==n-1)break;
    }
    e[1].k=-inf;e[num=1].x=0;
    fo(i,1,B){
        u=b[i].x,v=b[i].y;
        if(u==v)continue;
        makeroot(u),access(v);splay(v,0);
        l=p[v];if(mx[v]==-inf)continue;pp++;if(pp>=n)break;
        cut(a[l-n].x,l);cut(l,a[l-n].y);
        e[++num].y=b[i].k-a[l-n].k;e[num].x=i;
        e[num].k=b[i].k-a[l-n].k;
        link(b[i].x,n+A+i);link(n+A+i,b[i].y);
    }
    sort(e+1,e+1+num,cmp);
    fo(i,1,q)scanf("%lld",&s[i].k),s[i].x=i;
    sort(s+1,s+1+q,cmp);x=0;pp=n-1;
    fo(i,1,q){
        while(x+1<=num&&e[x+1].k<=s[i].k*2){
            x++,ans+=e[x].y;if(e[x].x)pp--;
        }
        an[s[i].x]=ans+(2*pp-n+1)*s[i].k;
    }
    fo(i,1,q)printf("%lld\n",an[i]);
}

你可能感兴趣的:(noip,LCT)