【LCT维护MST】JZOJ5433. 【NOIP2017提高A组集训10.28】图

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_43649416/article/details/101291366

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\n",Ans[i]);
}

int main(){
	n=read(),m1=read(),m2=read(),q=read();
	Predo(a,m1),Predo(b,m2);
	Maketree();
	ADD();
	GETANS();
}

猜你喜欢

转载自blog.csdn.net/qq_43649416/article/details/101291366