1 条题解

  • 0
    @ 2025-8-24 22:03:25

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar ldxcaicai
    **

    搬运于2025-08-24 22:03:25,当前版本为作者最后更新于2018-07-19 11:36:21,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    新鲜出炉的noi2018试题。 下面讲讲这题的解法: 首先要学习一个叫做kruskalkruskal重构树的东东。

    听名字就知道跟kruskalkruskal算法有关,没错,原来的kruskalkruskal算法就是用并查集实现的,但当我们使用kruskalkruskal重构树的时候,对于每次找出的不同的两个连通块的祖先,我们都新建一个点作为两个祖先的父亲,并将当前边的边权转化为新点的点权。然而,路径压缩的时候会让我们丢失这种辛辛苦苦创造的树的形状。。。因此我们需要在使用并查集维护连通性的同时使用二叉树来维护树的形状。这样维护出来的树就是kruskalkruskal重构树。

    不难发现kruskalkruskal重构树有几条重要的性质: 1.树上除叶子结点以外的点都对应着原来生成树中的边,叶子结点就是原来生成树上的节点。 2.由于新点的创建顺序与原来生成树上边权的大小有关,可以发现,从每个点到根节点上除叶子结点外按顺序访问到的点的点权是单调的。 3.出于kruskalkruskal算法贪心的性质,两个点uuvvlcalca的点权就对应着它们最小生成树上的瓶颈。 4.实际上这棵树就是一个二叉堆

    所以这道题如何用krukalkrukal重构树做呢?

    如果我们以海拔为第一关键字对边进行从大到小的排序,然后修建kruskalkruskal重构树,这样就弄出了一颗以海拔为关键字的小根堆。然后对于每一棵子树,如果询问中的水位线是低于子树的根节点的,那么此时这棵子树中的所有叶子结点都是连通的。放到题中就是说这颗子树中任选一个点出发,到子树中的其它点都不需要花费。

    然后我们假设对于当前询问,我们找到了一个子树的根节点uu,满足d[u]>pd[u]>pd[fa[u]]<=pd[fa[u]]<=p且出发点vv在子树中,这时从vv出发可以直接抵达子树中的任意一个叶子结点。因此我们需要从众多叶子节点中选出一个距离11号点花费最小的。

    然后再捋一捋思路。我们首先要求出每个点到11号点的最小花费,这个直接dijstradijstra+最短路预处理。然后是要建出kruskalkruskal重构树,再然后维护以每个点作为根节点时子树中距离11号点的最小花费,这个建完树后一个简单的dfsdfs搞定。最后是如何找到点uu,这时我们要让一个重要的算法登场:倍增算法。直接加上点权>p>p的限制在树上倍增即可。

    总时间复杂度OOTnlognT*nlogn)。

    代码如下:

    #include<bits/stdc++.h>
    #define N 400005
    #define M 800005
    using namespace std;
    inline int read(){
    	int ans=0;
    	char ch=getchar();
    	while(!isdigit(ch))ch=getchar();
    	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    	return ans;
    }
    inline void write(int x){
    	if(x>9)write(x/10);
    	putchar(x%10+'0');
    }
    int n,m,T,q,k,s,vis[N],first[N<<1],head[N],cntx=0,d[N],dep[N],f[N][20],fa[N<<1],lastans=0,totx=0;
    struct Node{int u,v,l,a;}e[M],p[N<<1];
    struct edge{int v,next;}tr[M<<1];
    struct node{int v,next,w;}t[M];
    struct heap{int u,v;};
    inline bool operator<(heap a,heap b){return a.v>b.v;}
    inline void dijstra(int s=1){
    	memset(vis,false,sizeof(vis));
    	memset(d,0x3f,sizeof(d));
    	priority_queue<heap>q;
    	d[s]=0;
    	q.push((heap){s,d[s]});
    	while(!q.empty()){
    		heap x=q.top();
    		q.pop();
    		if(vis[x.u])continue;
    		vis[x.u]=true;
    		for(int i=head[x.u];i;i=t[i].next){
    			int v=t[i].v;
    			if(vis[v])continue;
    			if(d[v]>d[x.u]+t[i].w){
    				d[v]=d[x.u]+t[i].w
    				;
    				q.push((heap){v,d[v]});
    			}
    		}
    	}
    	for(int i=1;i<=n;++i)p[i].l=d[i];
    }
    inline bool cmp(Node a,Node b){return a.a>b.a;}
    inline int find(int x){return x==fa[x]?fa[x]:fa[x]=find(fa[x]);}
    inline void add(int u,int v){
    	tr[++cntx].v=v;
    	tr[cntx].next=first[u];
    	first[u]=cntx;
    }
    inline void addx(int u,int v,int w){
    	t[++totx].v=v;
    	t[totx].next=head[u];
    	t[totx].w=w;
    	head[u]=totx;
    }
    inline void dfs(int u,int pa){
    	dep[u]=dep[pa]+1,f[u][0]=pa;
    	for(int i=1;i<=19;++i)f[u][i]=f[f[u][i-1]][i-1];
    	for(int i=first[u];i;i=tr[i].next){
    		int v=tr[i].v;
    		dfs(v,u);
    		p[u].l=min(p[u].l,p[v].l);
    	}
    }
    inline int query(int x,int y){
    	for(int i=19;i>=0;--i)if(dep[x]-(1<<i)>0&&p[f[x][i]].a>y)x=f[x][i];
    	return p[x].l;
    }
    inline void kruskal(){
    	int tot=0,cnt=n;
    	for(int i=1;i<=(n<<1);++i)fa[i]=i;
    	sort(e+1,e+m+1,cmp);
    	for(int i=1;i<=m;++i){
    		int u=e[i].u,v=e[i].v;
    		int fx=find(u),fy=find(v);
    		if(fx!=fy){
    			add(++cnt,fx);
    			add(cnt,fy);
    			fa[fx]=cnt;
    			fa[fy]=cnt;
    			p[cnt].a=e[i].a;
    			++tot;
    		}
    		if(tot==n-1)break;
    	}
    	dfs(cnt,0);
    	while(q--){
    		int x=(k*lastans+read()-1)%n+1,y=(k*lastans+read())%(s+1);
    		write(lastans=query(x,y));
    		puts("");
    	}
    }
    int main(){
    	T=read();
    	while(T--){
    		lastans=0,n=read(),m=read();
    		memset(e,0,sizeof(e)),cntx=0,totx=0;
    		memset(first,0,sizeof(first));
    		memset(head,0,sizeof(head));
    		memset(f,0,sizeof(f));
    		for(int i=1;i<=m;++i)e[i].u=read(),e[i].v=read(),e[i].l=read(),e[i].a=read(),addx(e[i].u,e[i].v,e[i].l),addx(e[i].v,e[i].u,e[i].l);
    		for(int i=n+1;i<=(n<<1);++i)p[i].l=0x3f3f3f3f;
    		dijstra();
    		q=read(),k=read(),s=read();
    		kruskal();
    	}
    	return 0;
    }
    
    • 1

    信息

    ID
    3764
    时间
    4000ms
    内存
    512MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者