P4234은 최소 차이를 스패닝 (LCT는 스패닝 트리를 유지)

그림 삽입 설명 여기


사이드 바이 중량에서 작은 대형

차이의 절반을 시작하려는 시점 열거 왼쪽 오른쪽 단정, 에지 간격이 스패닝 트리 구조에 의해서만 결정되고, 상기 슬라이더는 LCT + 일 수있다 log 2 n n \ 기록 2N ^ 완료 시간.

이제 더블 포인터, 복잡성을 고려하는 직접, 복잡성 아래로 슬라이딩 윈도우를 사용 n log n N N 로그 \ 각 이동의 오른쪽은 LCT에 추가 한 다음 중지 왼쪽 포인터가 바로 스패닝 트리를 형성 삭제 된하도록 가장자리. . .

당신은 약간의 연습이 두 배 포인터 SB, 오른쪽 끝 가장자리 점은 고리를 형성하고 추가 할 수 있습니다 찾을 생각이 시간이, 한쪽을 대체 경로의 최소한의 우측 상단이 최적 교체하도록 요청해야합니다.

광범위한 모습을 실제로 항상 오른쪽 포인터 이동 만 생각할 상기 LCT는 환을 형성하는 경우 가장자리에 첨가 한 후 최소 측 경로를 대체 어떠한 이중 포인터 천공의 좌측으로 이동하지 않아도 없을 때 스패닝 트리를 구축 할 수있을 때 그 대답은 업데이트됩니다.

이 방법을 요약하는 형태로, 이것은 최선의 선택 인 경우 경로에서 최대 값, 대체 링의 최소 값을 열거하는 것입니다.

마지막으로, 최대 보조 트리의 최저 온 부분의 오른쪽을 유지 명백하게 최대 값이 현재 열거 측, 단조롭게 증가하는 인덱스의 최소 값이고, 원래의 배열 포인터 스위프를 사용하여, I는 시간 또는 공간 관계없이 사용 MULTISET가 모두 소거 패스의 값이 오히려 반복자 삭제 아닌 값인 경우 그러한 모든 MULTISET의 값을 삭제할 것을 유의 전체 트리 에지 웨이트의 최대 값과 최소값을 유지하는 것이 더 열등 효과가 동등 설정에 따라서도 매우 SB 호출을 삭제 한 후 반복자를 검색 및 바이너리.

LCT는 이전 루틴을 가리 키도록 오른쪽 회전 측을 유지에 관한 것입니다.


코드 :

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6 + 10;
typedef long long ll;
#define pii pair<int,int>
#define fir first
#define sec second
int n,m,ans;
struct node {
	int u,v,w;
	bool operator < (const node &rhs) const {
		return w < rhs.w;
	}	
}E[maxn];
multiset<int> st;
inline int read(){
    int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
    if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}
struct LCT {						//用splay维护原森林的连通,用到了splay的操作以及数组 
	int ch[maxn][2];				//ch[u][0] 表示 左二子,ch[u][1] 表示右儿子
	int f[maxn];					//当前节点的父节点 
	int tag[maxn];					//翻转标记,乘标记,加标记 
	int top,sta[maxn],sz[maxn];
	int val[maxn],mi[maxn],len[maxn],sum[maxn];
	inline bool get(int x) {
    	return ch[f[x]][1] == x;
	}
	void init() {
		for (int i = 1; i <= n; i++)
			sz[i] = 1, mi[i] = i, val[i] = 2147483647;
	}
	inline void pushup(int rt) {
		if (rt) {
			sz[rt] = 1; mi[rt] = rt; sum[rt] = len[rt];
			int ls = ch[rt][0], rs = ch[rt][1];
			if (ls) {
				sz[rt] += sz[ls];
				sum[rt] += sum[ls];
				if (val[mi[ls]] < val[mi[rt]])
					mi[rt] = mi[ls];
			}
			if (rs) {
				sz[rt] += sz[rs];
				sum[rt] += sum[rs];
				if (val[mi[rs]] < val[mi[rt]])
					mi[rt] = mi[rs];
			}
		}
	}
	inline void pushdown(int rt) {
		if (tag[rt]) {
			int ls = ch[rt][0], rs = ch[rt][1];
			if (ls) swap(ch[ls][0],ch[ls][1]), tag[ls] ^= 1;
			if (rs) swap(ch[rs][0],ch[rs][1]), tag[rs] ^= 1;
			tag[rt] = 0;
		}
	}
	inline bool isroot(int x) {
		return (ch[f[x]][0] != x) && (ch[f[x]][1] != x);
	}
 	inline void rotate(int x) {							//旋转操作,根据 x 在 f[x] 的哪一侧进行左旋和右旋 
	    int old = f[x], oldf = f[old];
		int whichx = get(x);
		if(!isroot(old)) ch[oldf][ch[oldf][1] == old] = x;		//如果 old 不是根节点,就要修改 oldf 的子节点信息
	    ch[old][whichx] = ch[x][whichx ^ 1];
	    ch[x][whichx ^ 1] = old;
	    f[ch[old][whichx]] = old;
	    f[old] = x; f[x] = oldf;
		pushup(old); pushup(x); 
	}
	inline void splay(int x) {								//将 x 旋到所在 splay 的根
		top = 0; sta[++top] = x;
		for (int i = x; !isroot(i); i = f[i]) sta[++top] = f[i]; //在 splay 中维护 下推标记 
		while(top) pushdown(sta[top--]);
    	for(int fa = f[x]; !isroot(x); rotate(x), fa = f[x]) {	//再把x翻上来
        	if(!isroot(fa))										//如果fa非根,且x 和 fa是同一侧,那么先翻转fa,否则先翻转x 
            	rotate((get(x) == get(fa)) ? fa : x);
        }
	}
	inline void access(int x) {					//access操作将x 到 根路径上的边修改为重边 
		int lst = 0;
		while(x > 0) {
			splay(x);
			ch[x][1] = lst;
			pushup(x);
			lst = x; x = f[x];
		}
	}
	inline void move_to_root(int x) {			//将 x 移到 x 所在树的根(不是所在splay的根,所在splay只是一条重链) 
		access(x); splay(x); tag[x] ^= 1; swap(ch[x][0],ch[x][1]);
		//将 x 移到 根之后 x 是深度最低的点,这条重链、这棵splay上所有点的深度颠倒,
		//所有的点的左子树的点应该到右子树,因此要翻转这棵splay的左右子树
	}
	inline int findroot(int x) {
		access(x); 
		splay(x); 
		int rt = x;
		while(ch[rt][0]) rt = ch[rt][0];
		return rt;
	}
	inline void split(int x,int y) {
		move_to_root(x); access(y); splay(y);
	}
	inline void link(int x,int y) {
		move_to_root(x); f[x] = y; splay(x);
	}
	inline void cut(int x,int y) {
		split(x,y);
		ch[y][0] = f[x] = 0;
		pushup(y);
	}
}tree;
int p[maxn];
int find(int x) {
	return x == p[x] ? x : p[x] = find(p[x]);
}
int id,x,y,u,v,k[maxn],tot,vis[maxn];
char op[10];
int main() {
	n = read(); m = read();
	tree.init();
	tot = n;
	ans = 2147483647;
	for (int i = 1; i <= m; i++) {
		scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].w);
	}
	for (int i = 1; i <= n; i++) {
		p[i] = i;
	}
	sort(E + 1,E + m + 1);
	for (int i = 1; i <= m; i++) {
		int u = E[i].u, v = E[i].v, w = E[i].w;
		tree.sum[i + n] = tree.val[i + n] = w;
		tree.mi[i + n] = i + n;
		tree.sz[i + n] = 1; 
		if (u == v) continue;
		if (tree.findroot(u) == tree.findroot(v)) {
			tree.split(u,v);
			int p = tree.mi[v];
			tree.cut(E[p - n].u,p);
			tree.cut(E[p - n].v,p);
			auto it = st.lower_bound(E[p - n].w);
			st.erase(it);
			tree.link(u,i + n);
			tree.link(v,i + n);
			st.insert(E[i].w);
		} else {
			tree.link(u,i + n);
			tree.link(v,i + n);
			st.insert(E[i].w);
			tot--;
		}
		if (tot == 1) 
			ans = min(ans,*st.rbegin() - *st.begin());
	}
	printf("%d\n",ans);
	return 0;
}

게시 된 332 개 원래 기사 · 원 찬양 10 ·은 10000 +를 볼

추천

출처blog.csdn.net/qq_41997978/article/details/104370136