kruskal重构树

Kruskal重构树

第二次学了。
大致思路就是在最小生成树加边的时候,把每条边也设一个点,和他连着的两个点连边。由于最小生成树的贪心,感觉很像哈夫曼树,有性质是经过的边的长度(已经转化为点权)越向上越大/越小,取决于生成树的排序。那就可以通过倍增找不经过长度超过x的边所能走到的所有点,实际上是一直往上跳的那个子树。

		for (int i=1;i<=n;i++) bel[i]=i,top[i]=i;
		int num=n;
		for (int i=1;i<=m;i++)
		{
			int f1=find(edges[i].u),f2=find(edges[i].v);
			if (f1==f2) continue;
			++num;
			bel[f1]=f2;
			addedge(num,top[f1],edges[i].a);
			addedge(num,top[f2],edges[i].a);
			top[f2]=num;
		}

bel是并查集的fa,top是这一坨联通块现在的最上面的那个代表边的点的编号。fa正常合并,每次把top改成“新建的那个代表边的点”,再加边就向这个点连边即可。

题:NOI2018归程
题目链接
大意:每条边有一个海拔,多组询问,每次询问给出 v , p v,p v,p,海拔高于 p p p的边没有长度,问从 v v v号点走到 1 1 1号点的最短路。
思路:根据重构树的性质,做最大生成树,他的重构树保证越向上海拔越小,从 v v v倍增向上跳,一直跳到海拔小于 p p p,那么实际上能“免费”走到的点就应该是能向上跳的最顶上的点的那个子树。维护子树mindis即可。

#include<bits/stdc++.h>
#define pa pair<int,int>
#define INF 0x3f3f3f3f
#define inf 0x3f
#define fi first
#define se second
#define mp make_pair
#define ll long long
#define ull unsigned long long
#define pb push_back

using namespace std;

inline ll read()
{
	ll f=1,sum=0;char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}
	while (isdigit(c)) {sum=sum*10+c-'0';c=getchar();}
	return sum*f;
}
const int MAXN=500010;
const int MAXM=1000010;
struct Node{
	int u,v,a,l;
	bool operator < (const Node b) const {return a>b.a;}
}edges[MAXM];
int bel[MAXN],top[MAXN];
int find(int x)
{
	if (bel[x]!=x) bel[x]=find(bel[x]);
	return bel[x];
}
struct edge{
	int next,to,val;
}e[MAXM*2];
int head[3*MAXN],cnt;
void addedge(int u,int v,int w)
{
	e[++cnt].next=head[u];
	e[cnt].to=v;
	e[cnt].val=w;
	head[u]=cnt;
}
int dis[MAXN];
priority_queue <pa,vector<pa>,greater<pa> > q;
bool vis[MAXN];
void Dijkstra(int n)
{
	for (int i=1;i<=n;i++) dis[i]=INT_MAX,vis[i]=0;
	dis[1]=0;
	q.push(mp(0,1));
	while (!q.empty())
	{
		int x=q.top().se;
		q.pop();
		if (vis[x]) continue;
		vis[x]=1;
		for (int i=head[x];i;i=e[i].next)
		{
			int v=e[i].to;
			if (dis[v]>dis[x]+e[i].val) dis[v]=dis[x]+e[i].val,q.push(mp(dis[v],v));
		}
	}
}
const int LOG=25;
int f[MAXN][LOG],g[MAXN][LOG];
int mn[MAXN];
void dfs(int x,int n)
{
	if (x<=n) mn[x]=dis[x];
	else mn[x]=INT_MAX;
	for (int i=head[x];i;i=e[i].next)
	{
		int v=e[i].to;
		g[v][0]=e[i].val;
		f[v][0]=x;
		dfs(v,n);
		mn[x]=min(mn[x],mn[v]);
	}
}
int main()
{
	int T=read();
	while (T--)
	{
		memset(head,0,sizeof(head));
		cnt=0;
		int n=read(),m=read();
		for (int i=1;i<=m;i++) 
		{
			edges[i].u=read(),edges[i].v=read(),edges[i].l=read(),edges[i].a=read();
			addedge(edges[i].u,edges[i].v,edges[i].l);
			addedge(edges[i].v,edges[i].u,edges[i].l);
		}
		Dijkstra(n);
		memset(head,0,sizeof(head));
		cnt=0;
		sort(edges+1,edges+1+m);
		for (int i=1;i<=n;i++) bel[i]=i,top[i]=i;
		int num=n;
		for (int i=1;i<=m;i++)
		{
			int f1=find(edges[i].u),f2=find(edges[i].v);
			if (f1==f2) continue;
			++num;
			bel[f1]=f2;
			addedge(num,top[f1],edges[i].a);
			addedge(num,top[f2],edges[i].a);
			top[f2]=num;
		}
		dfs(num,n);
		f[num][0]=num;
		for (int j=1;j<LOG;j++) for (int i=1;i<=num;i++) f[i][j]=f[f[i][j-1]][j-1];
		for (int j=1;j<LOG;j++) for (int i=1;i<=num;i++) g[i][j]=g[f[i][j-1]][j-1];
		// for (int i=1;i<=num;i++,cout<<endl) for (int j=0;j<3;j++) cout<<f[i][j]<<' ';
		int q=read(),k=read(),s=read();
		int lastans=0;
		while (q--)
		{
			int v0=read(),p0=read();
			int v=(1ll*v0+1ll*k*lastans-1)%n+1;
			int p=(1ll*p0+1ll*k*lastans)%(s+1);
			// cout<<v<<"--"<<endl;
			for (int i=LOG-1;i>=0;i--)
				if (g[v][i]>p) v=f[v][i];//cout<<v<<' '<<i<<' '<<f[v][i]<<' '<<g[v][i]<<endl;
			// cout<<v<<endl;
			lastans=mn[v]; 
			cout<<lastans<<'\n';
		}
	}
	return 0;
}

题:洛谷P7834 [ONTAK2010] Peaks 加强版
题目链接
大意:无向图,强制在线多组询问从 u u u点出发不经过权值 > x > x >x的边,能走到的点的点权第 k k k大是多少。
思路:重构树+倍增向上跳找到子树根,转化为求子树根的第 k k k大,经典dfs序上主席树。

#include<bits/stdc++.h>
#define pa pair<int,int>
#define INF 0x3f3f3f3f
#define inf 0x3f
#define fi first
#define se second
#define mp make_pair
#define ll long long
#define ull unsigned long long
#define pb push_back

using namespace std;

inline ll read()
{
	ll f=1,sum=0;char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}
	while (isdigit(c)) {sum=sum*10+c-'0';c=getchar();}
	return sum*f;
}
const int MAXN=300010;
const int MAXM=1000010;
struct edge{
	int next,to;
}e[MAXM*4];
int head[MAXN],cnt;
void addedge(int u,int v)
{
	e[++cnt].next=head[u];
	e[cnt].to=v;
	head[u]=cnt;
}
struct Node{
	int u,v,w;
	bool operator < (const Node b) const {return w<b.w;}
}edges[MAXM];
int a[MAXN],fa[MAXN],top[MAXN],val[MAXN];
int find(int x)
{
	if (fa[x]!=x) fa[x]=find(fa[x]);
	return fa[x];
}
const int LOG=25;
int sz[MAXN],f[MAXN][LOG],g[MAXN][LOG],l[MAXN],r[MAXN];
int tots;
struct Point{
	int lch,rch,val;
}tr[MAXN*LOG];
int root[MAXN],treenum;
int modify(int pre,int left,int right,int x)
{
	int now=++treenum;
	if (left==right)
	{
		tr[now].lch=tr[now].rch=0;
		tr[now].val=tr[pre].val+1;
		return now;
	}
	int mid=(left+right)>>1;
	if (x<=mid)
	{
		tr[now].lch=modify(tr[pre].lch,left,mid,x);
		tr[now].rch=tr[pre].rch;
	}
	else
	{
		tr[now].lch=tr[pre].lch;
		tr[now].rch=modify(tr[pre].rch,mid+1,right,x);
	}
	tr[now].val=tr[tr[now].lch].val+tr[tr[now].rch].val;
	return now;
}
int query(int root1,int root2,int left,int right,int k)
{
	if (left==right) return left;
	int mid=(left+right)>>1;
	if (tr[tr[root2].lch].val-tr[tr[root1].lch].val>=k) return query(tr[root1].lch,tr[root2].lch,left,mid,k);
	else return query(tr[root1].rch,tr[root2].rch,mid+1,right,k-tr[tr[root2].lch].val+tr[tr[root1].lch].val);
}
void dfs_order(int x,int n)
{
	if (x<=n)
	{
		l[x]=r[x]=++tots;
		root[tots]=modify(root[tots-1],1,n,a[x]);
		return ;
	}
	int i=head[x];
	dfs_order(e[i].to,n);
	l[x]=l[e[i].to];
	i=e[i].next;
	dfs_order(e[i].to,n);
	r[x]=r[e[i].to];
}
void dfs(int x)
{
	for (int i=head[x];i;i=e[i].next)
	{
		int v=e[i].to;
		f[v][0]=x,g[v][0]=val[x];
		dfs(v);
		sz[x]+=sz[v];
	}
	if (!sz[x]) sz[x]=1;
}
vector <int> vals;
int main()
{
	int n=read(),m=read(),q=read();
	for (int i=1;i<=n;i++) a[i]=read(),vals.push_back(a[i]);
	sort(vals.begin(),vals.end());
	int newsize=unique(vals.begin(),vals.end())-vals.begin();
	vals.resize(newsize);
	for (int i=1;i<=n;i++) a[i]=lower_bound(vals.begin(),vals.end(),a[i])-vals.begin()+1;
	for (int i=1;i<=m;i++) edges[i].u=read(),edges[i].v=read(),edges[i].w=read();
	sort(edges+1,edges+1+m);
	for (int i=1;i<=n;i++) fa[i]=i,top[i]=i;
	int num=n;
	for (int i=1;i<=m;i++)
	{
		int f1=find(edges[i].u),f2=find(edges[i].v);
		if (f1==f2) continue;
		++num;
		fa[f1]=f2;
		addedge(num,top[f1]),addedge(num,top[f2]);
		// cout<<f1<<" "<<f2<<" "<<num<<' '<<top[f1]<<" "<<top[f2]<<endl;
		val[num]=edges[i].w;
		top[f2]=num;
	}
	dfs(num);
	f[num][0]=num,g[num][0]=val[num];
	// for (int i=1;i<=num;i++) cout<<i<<' '<<sz[i]<<endl;
	for (int j=1;j<LOG;j++) for (int i=1;i<=num;i++) f[i][j]=f[f[i][j-1]][j-1];
	for (int j=1;j<LOG;j++) for (int i=1;i<=num;i++) g[i][j]=g[f[i][j-1]][j-1];
	// for (int i=1;i<=num;i++,cout<<endl) for (int j=0;j<5;j++) cout<<f[i][j]<<' ';
	int lstans=0;
	dfs_order(num,n);
	while (q--)
	{
		int _u=read(),_x=read(),_k=read();
		int u=(_u^lstans)%n+1,x=_x^lstans,k=(_k^lstans)%n+1;
		for (int j=LOG-1;j>=0;j--) if (g[u][j]<=x) u=f[u][j];
		if (sz[u]<k)
		{
			cout<<"-1\n";
			lstans=0;
			continue;
		}
		k=sz[u]-k+1;
		int ret=query(root[l[u]-1],root[r[u]],1,n,k);
		lstans=vals[ret-1];
		cout<<lstans<<'\n';
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值