【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
#include
#include
#include
#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'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

你可能感兴趣的:(题解,数据结构,LCT)