【NOIP2017提高A组集训10.28】图

来源:互联网 发布:泰牛程序员官网 编辑:程序博客网 时间:2024/05/17 10:07

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<iostream>#include<algorithm>#include<string.h>#include<stdio.h>#include<math.h>#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.k<y.k||x.k==y.k&&x.y<y.y;}int 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]);}
阅读全文
1 0
原创粉丝点击