kruskal重构树

经常碰到一类题目,让你在一个具有某种特殊性质(边权>=/<=x)的连通块内做一些操作,有多个询问。

这时候就希望可以找出一种数据结构,使得每个连通块都可以恰好变成一棵子树,然后维护维护就可以了。

能不能找到呢?当然是可以的。

这就是kruskal重构树。在做kruskal的过程中,将加入的边建成一个点,作为它连接的两个集合的父亲。

这样造出的树通常是个大根堆/小根堆,且它和最小生成树的关系非常密切。

noi2018归程

突然发现如果会kruskal重构树的话此题就是Noi2018最简单的一题了。。屠龙勇士至少细节还是比较坑的。。

此题就是找出走>p的边能够到达的所有点,对它们的dis[1]取个min。就是kruskal生成树的裸题。

#include<bits/stdc++.h>
using namespace std;
#define rep(x,y,z) for (int x=y; x<=z; x++)
#define downrep(x,y,z) for (int x=y; x>=z; x--)
#define ms(x,y,z) memset(x,y,sizeof(z))
#define LL long long
#define repedge(x,y) for (int x=hed[y]; ~x; x=edge[x].nex)
inline int read(){
	int x=0; int w=0; char ch=0;
	while (ch<'0' || ch>'9') w|=ch=='-',ch=getchar();
	while (ch>='0' && ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return w? -x:x;
}
#define mp make_pair
#define fr first
#define se second
#define pr pair<LL,int>
const LL inf=4e9;
const int N=400005;
const int M=400005;
int nedge,n,m,Nedge,head[N],hed[N],vis[N],root,cnt,f[N],F[N][25];
LL dis[N],w[N],val[N];
struct node{ int a,b,len,het; }e[M];
bool operator < (node x,node y){ return (x.het>y.het); }
struct Edge{ int to,nex,cst; }edge[M<<1],E[M<<1];
priority_queue< pr ,vector< pr >,greater< pr> > q;
void addedge(int a,int b,int c){
	edge[nedge].to=b; edge[nedge].nex=hed[a]; edge[nedge].cst=c; hed[a]=nedge++;
}
void dijkstra(int s){
	rep(i,1,n) dis[i]=inf; dis[s]=0; ms(vis,0,vis); while (!q.empty()) q.pop(); q.push(mp(dis[s],s));
	while (!q.empty()){
		pr x=q.top(); q.pop(); int k=x.se; if (vis[k]) continue; vis[k]=1;
		repedge(i,k){ int v=edge[i].to; LL tmp=dis[k]+edge[i].cst;
		   if (tmp<dis[v]){ dis[v]=tmp; q.push(mp(dis[v],v)); }
		}
	}
}
#define repE(x,y) for(int x=head[y]; ~x; x=E[x].nex)
void addE(int a,int b){
	E[Nedge].to=b; E[Nedge].nex=head[a]; head[a]=Nedge++;
}
int gf(int k){ return (k==f[k])? k:f[k]=gf(f[k]); }
void build_kruskal(){
	sort(e+1,e+m+1); rep(i,1,2*n) f[i]=i; cnt=n;
	Nedge=0; ms(head,-1,head);
	rep(i,1,m){
		int fa=gf(e[i].a); int fb=gf(e[i].b);
		if (fa==fb) continue;
		f[fa]=f[fb]=++cnt; w[cnt]=e[i].het;
		addE(cnt,fa); addE(cnt,fb); 
	}
	root=cnt;
}
void dfs(int k){
	val[k]=(k<=n)? dis[k]:inf; 
	repE(i,k){
		int v=E[i].to; F[v][0]=k; dfs(v);
		val[k]=min(val[k],val[v]);
	}
}
LL solve(int v,int p){
	downrep(i,20,0) if (w[F[v][i]]>p) v=F[v][i];
	return val[v];
}
int main(){
	int cas=read();
	rep(cl,1,cas){
	  n=read(); m=read(); nedge=0; ms(hed,-1,hed);
	  rep(i,1,m) e[i].a=read(),e[i].b=read(),e[i].len=read(),e[i].het=read();
      rep(i,1,m) addedge(e[i].a,e[i].b,e[i].len),addedge(e[i].b,e[i].a,e[i].len);
      dijkstra(1); build_kruskal(); 
	  F[root][0]=root; dfs(root); rep(j,1,20) rep(i,1,cnt) F[i][j]=F[F[i][j-1]][j-1];
      int q=read(); int K=read(); int S=read(); LL ans=0;
      rep(i,1,q){
    	 int v=read(); LL p=read();
    	 v=(v+K*ans-1)%n+1; p=(p+K*ans)%(S+1);
    	 ans=solve(v,p); printf("%lld\n",ans); 
	  }
    }
    return 0;
}

UPD:bzoj3551 peaks强制在线。(3545非强制在线,可以用排序+启发式合并)

套个主席树就好了。

#include<bits/stdc++.h>
using namespace std;
#define rep(x,y,z) for (int x=y; x<=z; x++)
#define downrep(x,y,z) for (int x=y; x>=z; x--)
#define ms(x,y,z) memset(x,y,sizeof(z))
#define LL long long
#define repedge(x,y) for (int x=hed[y]; ~x; x=edge[x].nex)
inline int read(){
	int x=0; int w=0; char ch=0;
	while (ch<'0' || ch>'9') w|=ch=='-',ch=getchar();
	while (ch>='0' && ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return w? -x:x;
}
const int N=200005;
const int M=500005;
int root,val[N],n,m,q,hed[N],nedge,f[N],cnt,pres[N],h[N],w[N],F[N][25],tin[N],tout[N],rot[N],tot,dt;
map<int,int> mat,rk;
struct Edge{ int to,nex; }edge[M<<1];
struct node{ int a,b,c; }e[M];
bool operator < (node x,node y){ return (x.c<y.c); }
struct Segment{ int sum,lson,rson; }tr[N*20];
void addedge(int a,int b){
	edge[nedge].to=b; edge[nedge].nex=hed[a]; hed[a]=nedge++;
}
int gf(int k){ return (k==f[k])? k:f[k]=gf(f[k]); }
void build_kruskal(){
	nedge=0; ms(hed,-1,hed);
	sort(e+1,e+m+1); rep(i,1,2*n) f[i]=i; cnt=n;
	rep(i,1,m){
		int fa=gf(e[i].a); int fb=gf(e[i].b);
		if (fa==fb) continue;
		f[fa]=f[fb]=++cnt; w[cnt]=e[i].c;
		addedge(cnt,fa); addedge(cnt,fb);
	}
	root=cnt;
}
void dfs(int k){
	if (k<=n){ tin[k]=tout[k]=++tot; val[tot]=h[k]; }
	repedge(i,k){
		int v=edge[i].to;
		F[v][0]=k; dfs(v); tin[k]=(!tin[k])? tin[v]:min(tin[v],tin[k]);
		tout[k]=max(tout[k],tout[v]);		
	}
}
#define mid ((l+r)>>1)
#define lc (tr[nod].lson)
#define rc (tr[nod].rson)
#define flc (tr[fnod].lson)
#define frc (tr[fnod].rson)
#define fr first
#define se second
void pushup(int nod){ tr[nod].sum=tr[lc].sum+tr[rc].sum; }
void build(int l,int r,int &nod){
	nod=++dt; if (l==r){ tr[nod].sum=0; return; }
	build(l,mid,lc); build(mid+1,r,rc); pushup(nod);
}
void upd(int l,int r,int fnod,int &nod,int x){
    nod=++dt; tr[nod]=tr[fnod]; if (l==r){ tr[nod].sum++; return; }
    if (x<=mid) upd(l,mid,flc,lc,x); else upd(mid+1,r,frc,rc,x);
    pushup(nod);
}
void build_segment(){
	rep(i,1,n) mat[val[i]]++; int tmp=0;
	for(map<int,int> :: iterator i=mat.begin(); i!=mat.end(); i++)
	{ rk[(i->fr)]=++tmp; pres[tmp]=(i->fr); }
	dt=rot[0]=0; build(1,n,rot[0]); 
	rep(i,1,n) upd(1,n,rot[i-1],rot[i],rk[val[i]]);
}
int query(int l,int r,int fnod,int nod,int x){
	if (l==r) return (tr[nod].sum-tr[fnod].sum>=x)? pres[l]:(-1);
	return (x<=tr[lc].sum-tr[flc].sum)? query(l,mid,flc,lc,x):
	query(mid+1,r,frc,rc,x-(tr[lc].sum-tr[flc].sum));
}
int que(int l,int r,int k){
	int sum=tr[rot[r]].sum-tr[rot[l-1]].sum;
	return (k>sum)? (-1):query(1,n,rot[l-1],rot[r],sum-k+1);
}
int solve(int v,int x,int k){
	downrep(i,20,0) if (w[F[v][i]]<=x) v=F[v][i];
	return que(tin[v],tout[v],k);
}
int main(){
	n=read(); m=read(); q=read(); rep(i,1,n) h[i]=read();
	rep(i,1,m) e[i].a=read(),e[i].b=read(),e[i].c=read();
	build_kruskal(); F[root][0]=root; dfs(root); 
	rep(j,1,20) rep(i,1,cnt) F[i][j]=F[F[i][j-1]][j-1]; build_segment(); int ans=0;
	rep(i,1,q){
		int v=read(); int x=read(); int k=read();
		if (ans!=-1){ v^=ans; x^=ans; k^=ans; }
		ans=solve(v,x,k); printf("%d\n",ans);
	}
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值