zoukankan      html  css  js  c++  java
  • 【LCT维护MST】JZOJ5433. 【NOIP2017提高A组集训10.28】图

    Description

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

    Solution

    • 比赛时读优没有读入负号而爆零了
    • 显然是分成正边树和负边树。其他不在最小生成树中的边都是没有用的。
    • 当x无限小的时候,一定取正边树,当x无限大的时候一定取负边树。
    • 所以在这之中就存在一个将正边替换为负边的过程。
    • 形象地考虑一下做最小生成树的过程,是将所有边排序。
    • 当x是无限小的时候,正边边权的值域[l0,r0]与负边边权的值域[l1,r1]没有交。
    • 但是当x逐渐变大的时候,正边中的有些边的边权比负边边权要大,而最先比某个正边边权要小的负边一定是k最小的。而它能替代的边理解成最小生成树的逆过程,肯定是替换掉最后加进去的,也就是k最大的正边。而且一条负边一定不会替换掉负边,这样一定不会更优。
    • 所以我们只需要按照负边从小到大替换掉正边,对于每一个正边都在某一个时刻x被替换(意味着如果x在这个时刻后,那么这条边就被删掉,并加入了一条负边)。
    • 用一个LCT维护MST的过程就好了。
    • LCT时将边转化成点。查询x,y最大的边时,makeroot(x),access(y),那么x,y这条链就在splay树(x)中了。
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define maxn 100005
    #define maxm 200005
    #define maxt 1000005
    #define inf (1e9+5)
    #define ll long long 
    using namespace std;
    
    const int null=0;
    int n,m1,m2,q,i,j,k,fa[maxn],cnt,p[maxm],pi[maxm],que[maxn],qi[maxn];
    struct edge{int x,y,z;} a[maxm],b[maxm];
    int cmp(edge a,edge b){return a.z<b.z;}
    int cmp2(int i,int j){return p[i]-a[i].z<p[j]-a[j].z;}
    int cmp3(int i,int j){return que[i]<que[j];}
    
    struct node{
        int f,s[2],tag,mx,a;
    } t[maxt];
    int nroot(int x){return t[t[x].f].s[0]==x||t[t[x].f].s[1]==x;}
    int get(int x){return t[t[x].f].s[1]==x;}
    void update(int x){
    	int l=t[x].s[0],r=t[x].s[1]; t[x].mx=t[x].a;
    	if (l) t[x].mx=max(t[x].mx,t[l].mx);
    	if (r) t[x].mx=max(t[x].mx,t[r].mx);
    }
    void downtag(int x){
    	if (t[x].tag) {
    		swap(t[x].s[0],t[x].s[1]);
    		if (t[x].s[0]) t[t[x].s[0]].tag^=1;
    		if (t[x].s[1]) t[t[x].s[1]].tag^=1;
    		t[x].tag=0;
    	}
    }
    void rotate(int x){
        int y=t[x].f,c=get(x);
        t[y].s[c]=t[x].s[c^1]; t[x].s[c^1]=y;
        if (t[y].s[c]!=null) t[t[y].s[c]].f=y;
        if (nroot(y)) t[t[y].f].s[get(y)]=x;
        t[x].f=t[y].f; t[y].f=x;
        update(y); update(x);
    }
    int d[maxn];
    void remove(int x){
        while (x!=null){d[++d[0]]=x;if (!nroot(x)) break; x=t[x].f;}
        while (d[0]) downtag(d[d[0]--]);
    }
    void splay(int x){
        remove(x);
        int y;
        while (nroot(x)){
            y=t[x].f;
            if (nroot(y)){
                if (get(x)==get(y)) rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        update(x);
    }
    void access(int x){
        int y=null;
        for(;x!=null;x=t[x].f){
            splay(x);
            t[x].s[1]=y;
            update(y=x);
        }
    }
    void makeroot(int x){access(x);splay(x);t[x].tag=1;}
    void link(int x,int y){makeroot(x);t[x].f=y;access(x);}
    void cut(int x,int y){makeroot(x);access(y);splay(y);t[t[y].s[0]].f=null;t[y].s[0]=null;update(y);}
    
    int read(){
    	int x=0,tp=1; char ch=getchar();
    	for(;(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    	if (ch=='-') tp=-1,ch=getchar();
    	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    	return x*tp;
    }
    
    int father(int x){return (fa[x]==x)?x:fa[x]=father(fa[x]);}
    
    int link_fa(edge e){
    	int x=father(e.x),y=father(e.y);
    	if (x!=y) {fa[x]=y;return 1;}
    	return 0;
    }
    
    void Predo(edge *a,int m){
    	for(i=1;i<=m;i++) a[i].x=read(),a[i].y=read(),a[i].z=read();	
    	sort(a+1,a+1+m,cmp);
    	for(i=1;i<=n;i++) fa[i]=i; cnt=0;
    	for(i=1;i<=m;i++) if (link_fa(a[i])){
    		a[++cnt]=a[i];
    		if (cnt==n-1) break;
    	}
    }
    
    void Maketree(){
    	for(i=1;i<=3*n-2;i++) t[i].mx=t[i].a=-inf;
    	for(i=1;i<n;i++) {
    		int x=a[i].x,y=a[i].y; t[n+i].mx=t[n+i].a=a[i].z;
    		link(x,n+i),link(n+i,y);
    	}
    }
    
    int find(int x){
    	downtag(x);
    	if (t[x].a==t[x].mx) return x;
    	int l=t[x].s[0],r=t[x].s[1];
    	if (l&&t[l].mx==t[x].mx) return find(l);
    	else return find(r);
    }
    
    void ADD(){
    	for(i=1;i<n;i++) {
    		int x=b[i].x,y=b[i].y;
    		makeroot(x),access(y);
    		splay(x); 
    		int k=find(x);
    		int xx=a[k-n].x,yy=a[k-n].y;
    		cut(k,xx),cut(k,yy);
    		p[k-n]=b[i].z;
    		link(n+(n-1)+i,x),link(n+(n-1)+i,y);
    	}
    }
    
    ll Ans[maxn];
    void GETANS(){
    	for(i=1;i<n;i++) pi[i]=i;
    	sort(pi+1,pi+n,cmp2);
    	for(i=1;i<=q;i++) que[i]=read(),qi[i]=i;
    	sort(qi+1,qi+1+q,cmp3);
    	ll ans=0; k=1;
    	for(i=1;i<n;i++) ans+=a[i].z;
    	for(i=1;i<=q;i++){
    		for(;k<n&&p[pi[k]]-a[pi[k]].z<que[qi[i]]*2;k++) ans+=p[pi[k]]-a[pi[k]].z;
    		Ans[qi[i]]=ans+1ll*que[qi[i]]*((n-1-k+1)-(k-1));
    	}
    	for(i=1;i<=q;i++) printf("%lld
    ",Ans[i]);
    }
    
    int main(){
    	n=read(),m1=read(),m2=read(),q=read();
    	Predo(a,m1),Predo(b,m2);
    	Maketree();
    	ADD();
    	GETANS();
    }
    
    
  • 相关阅读:
    Confluence无法打开编辑器,一直在转圈
    Xamarin.Forms中的ListView的ItemTrapped事件与ItemSelected事件的区别
    C#读取物理网卡信息及其对应的IP地址
    【Xamarin报错】visual studio android 模拟器部署卡住
    【Xamarin报错】AndroidManifest.xml : warning XA0101: @(Content) build action is not supported
    【Xamarin报错】 COMPILETODALVIK : UNEXPECTED TOP-LEVEL error java.lang.OutOfMemoryError: Java heap space
    【Xamarin报错】libpng warning : iCCP: Not recognizing known sRGB profile that has been edited
    子窗口调用父窗口
    Windows Phone 8.1 多媒体(3):音乐
    Windows Phone 8.1 多媒体(1):相片
  • 原文地址:https://www.cnblogs.com/DeepThinking/p/13090954.html
Copyright © 2011-2022 走看看