【bzoj 4046 加强版】Pork barrel

刚考完以为是神仙题……后来发现好像挺蠢的…… QwQ

题意

  给你一张 (n) 个点 (m) 条边的无向图(不一定连通),有 (q) 组询问,每组询问给你 (2) 个正整数 (l,h),你需要选出一些边,满足边权都在 ([l,h]) 范围内,连通尽量多的点对,在此基础上使得边权和最小。
  (1le n,m,qle 10^5,space 1le w_i,l,hle 10^4)(原题 (nle 1000,space qle 10^6)

题解

  动态最小生成树(汗)
  我怎么什么都没写过
  考场上花 10 分钟写了 5 分 (O(qn)) 暴力就滚了

  假设这题可以离线,先将所有边按权值从大到小排序。
  依次判断每条边的两端点是否连通,若未连通则连上(这样连通的点对数必定增多),若连通则替换掉一条树上这两点之间的简单路径上 权值最大的一条边(( ext{cut}) 一下权值最大的那条边,( ext{link}) 一下这条边)。
  加边的同时动态处理询问。对于一组询问 ([L,R]),若当前插入的边的权值是 (ge L) 的最小值,则我们决定处理这组询问。此时最小生成(森林)上的所有边权都 (ge L),我们直接扔掉所有权值 (gt R) 的边,剩余的边的权值和就是答案。发现边权很小,所以可以用树状数组动态维护最小生成(森林)中的边权,第 (i) 位记录所有权值为 (i) 的边的权值和,单点修改,区间查询。

  考虑在线,我想这题时居然忘了加边和询问是分开的了……
  依然把边按权值从大到小排序,每加入一条边时,第 (i) 个版本继承第 (i+1) 个版本,在主席树的第 (w_i) 个版本上单点修改即可。在线处理询问 ([L,R]) 时在主席树的第 (L) 个版本上区间查询即可。
  复杂度 (O(nlog n))
  仔细想想好像确实挺水的,所以还是我太菜了

  附注:
  1. (LCT) 一般维护点权,边权不太好维护,可以把第 (i) 条边转成一个编号为 (i+n) 的点,然后加入这条边的时候 把这个点与边的两端点 ( ext{link}) 一下即可
  2. 注意主席树的空间是 (O(2mlog w)) 的!!!不是 (O(10000log 10000))!!!因为这个 sb 问题调了一小时 zbl

#include<bits/stdc++.h>
#define N 200010
using namespace std;
inline int read(){
	int x=0; bool f=1; char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
	for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
	if(f) return x; return 0-x;
}
int n,m,online,q;
struct inp{int u,v,w;}a[N<<1];
inline bool cmp(inp a, inp b){
	return a.w>b.w;
}
namespace LCT{
	#define son(x,fx) tr[x].son[fx]
	struct Tree{int fa,son[2],v,mx_id; bool r;}tr[N<<1];
	inline bool isRoot(int x){
		return son(tr[x].fa,0)!=x && son(tr[x].fa,1)!=x;
	}
	inline bool idf(int x){
		return son(tr[x].fa,1)==x;
	}
	void pushup(int x){
		tr[x].mx_id=x;
		if(tr[tr[son(x,0)].mx_id].v > tr[tr[x].mx_id].v) tr[x].mx_id=tr[son(x,0)].mx_id;
		if(tr[tr[son(x,1)].mx_id].v > tr[tr[x].mx_id].v) tr[x].mx_id=tr[son(x,1)].mx_id;
	}
	void pushdown(int x){
		if(tr[x].r){
			swap(son(x,0),son(x,1));
			tr[son(x,0)].r^=1, tr[son(x,1)].r^=1;
			tr[x].r=0;
		}
	}
	void pd(int x){
		if(!isRoot(x)) pd(tr[x].fa);
		pushdown(x);
	}
	inline void connect(int x, int f, int fx){
		tr[x].fa=f, son(f,fx)=x;
	}
	void rotate(int x){
		int y=tr[x].fa, z=tr[y].fa, idf_x=idf(x), idf_y=idf(y), B=son(x,idf_x^1);
		if(!isRoot(y)) connect(x,z,idf_y);
		else tr[x].fa=z;
		connect(B,y,idf_x), connect(y,x,idf_x^1);
		pushup(y), pushup(x);
	}
	void splay(int x){
		pd(x);
		int f;
		for(; !isRoot(x); ){
			f=tr[x].fa;
			if(!isRoot(f)) rotate(idf(x)==idf(f) ? f : x);
			rotate(x);
		}
	}
	void access(int x){
		for(int y=0; x; x=tr[y=x].fa){
			splay(x);
			son(x,1)=y;
			pushup(x);
		}
	}
	void makeroot(int x){
		access(x), splay(x), tr[x].r^=1;
	}
	void link(int x, int y){
		makeroot(x);
		tr[x].fa=y;
	}
	int cutMax(int x, int y){
		makeroot(x);
		access(y);
		splay(y);
		x=tr[y].mx_id;
		splay(x);
		tr[son(x,0)].fa=tr[son(x,1)].fa=0;
		son(x,0)=son(x,1)=0;
		return x;
	}
	#undef son
}using namespace LCT;
namespace PT{
	#define son(x,fx) tr[o].son[fx]
	int cnt,rt[100010];
	struct Tree{int son[2],sum;}tr[200010*20];
	void ins(int &o, int pre, int l, int r, int x, int v){
		tr[o=++cnt] = tr[pre], tr[o].sum += v;
		if(l==r) return;
		int mid=l+r>>1;
		if(x<=mid) ins(son(o,0),son(pre,0),l,mid,x,v);
		else ins(son(o,1),son(pre,1),mid+1,r,x,v);
	}
	int query(int o, int l, int r, int L, int R){
		if(!o) return 0;
		if(L<=l && r<=R) return tr[o].sum;
		int mid=l+r>>1, ret=0;
		if(L<=mid) ret+=query(son(o,0),l,mid,L,R);
		if(R>mid) ret+=query(son(o,1),mid+1,r,L,R);
		return ret;
	}
}using namespace PT;
namespace DSU{
	int fa[N];
	void init(){
		for(int i=1; i<=n; ++i) fa[i]=i;
	}
	int find(int x){
		if(fa[x]==x) return x;
		return fa[x]=find(fa[x]);
	}
}using namespace DSU;
int main(){
	n=read(), m=read(), online=read();
	for(int i=1; i<=m; ++i) a[i].u=read(), a[i].v=read(), a[i].w=read();
	sort(a+1,a+m+1,cmp);
	init();
	int fu,fv;
	for(int i=1; i<=m; ++i){
		rt[a[i].w]=rt[a[i-1].w];
		fu=find(a[i].u), fv=find(a[i].v);
		if(fu==fv){
			q=cutMax(a[i].u,a[i].v);
			ins(rt[a[i].w],rt[a[i].w],1,a[1].w,LCT::tr[q].v,-LCT::tr[q].v);
		}
		else{
			DSU::fa[fv]=fu;
			q=n+i;
		}
		LCT::tr[q].v=a[i].w;
		LCT::tr[q].mx_id=q;
		link(a[i].u,q), link(q,a[i].v);
		ins(rt[a[i].w],rt[a[i].w],1,a[1].w,a[i].w,a[i].w);
	}
	for(int i=a[1].w-1; i>0; --i)
		if(rt[i]==0) rt[i]=rt[i+1];
	
	q=read();
	int l,r,lastans=0;
	while(q--){
		l=read()-online*lastans, r=read()-online*lastans;
		printf("%d
",lastans=query(rt[l],1,a[1].w,1,r));
	}
	return 0;
}
/*
5 7 1
1 2 2
2 3 4
3 4 3
4 5 1
5 1 3
2 5 4
1 4 5
5
1 2
4 7
11 12
11 13
18 19
*/
原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/bzoj4046.html