zoukankan      html  css  js  c++  java
  • 「NOI 2018」归程

    题目描述

    传送门

    解法

    ( ext{Method 1})( m Kruskal) 重构树

    先讲一下 ( m Kruskal) 重构树是个什么东西。

    它适用于求解这样的问题:在无向图中,从 (u) 出发只经过边权不超过/不低于阈值 (w) 的边能到达的节点。

    举个栗子,假设我们想要求解图中 (u,v) 之间所有路径的最大边权的最小值。先将边权升序排序,对于边 ((u,v,w)),我们查找 (u,v) 连通块的根 (x,y)(这个可以用并查集维护)。如果 (x,y) 不在一个连通块内,建一个虚点 (z),它的点权是 (w),并将其作为 (x,y) 的父亲。这样对于询问 ((u,v)),查询 ( m lca) 的点权即可。

    回到这道题。我们以海拔对边降序排序,建出 ( m Kruskal) 重构树。对于询问 ((v,p)),我们找到满足 (val_u>p,val_{fa_u}le p)(u)(u)(v) 的祖先),那么 (u) 子树内的点就是 (v) 可以免费到达的点。可以预处理出 (1) 到每个点的最短路,再维护一下 (u) 子树的点到 (1) 的最短路即可。

    复杂度 (mathcal O((m+q)cdot log ncdot T))

    ( ext{Method 2}):可持久化并查集

    首先可以想出一个离线的做法:将询问海拔降序排序,边也降序排序,每次加入新露出水面的边,和上面一样维护连通块与 (1) 的最短路即可。

    我们可以用可持久化并查集维护每种海拔对应的边集的连通块,不过是两个 $log $ 的。

    考虑到我们并不需要修改历史版本,可以用另一种方法 —— 半持久化并查集。

    说得更明白一点,可持久化并查集是在时间段内查询点,半持久化并查集是在点中查询时间段。将边降序排序,对于每个点用 ( m vector) 存一个类似单调队列的东西,保证海拔递减,但最小值递减,代表的是它的子树贡献的最短路。在连接边 ((u,v,w)) 时,查找 (u,v) 连通块的根 (x,y),若令 (x)(y) 的父亲,那么将 (y) 对应的 ( m vector) 中的最短路插入 (x),因为此时 (y) 对应的 ( m vector) 保存的海拔已经不是瓶颈,而变成了 (w)。答案要求最短路,肯定是插入最优的路径。

    询问的时候先跳到没被水淹的深度最低的父亲,再在它的 ( m vector) 里二分即可。这就变成了一个 $log $。

    代码

    只敲了半持久化并查集(逃

    #include <cstdio>
    #define print(x,y) write(x),putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while((s=getchar())>'9' or s<'0')
    		f|=(s=='-');
    	while(s>='0' and s<='9')
    		x=(x<<1)+(x<<3)+(s^48),
    		s=getchar();
    	return f?-x:x;
    }
    
    template <class T>
    inline void write(const T x) {
    	if(x<0) {
    		putchar('-'),write(-x);
    		return;
    	}
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    }
    
    #include <queue>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int maxn=2e5+5,maxm=4e5+5;
    
    int n,m,cnt,head[maxn],dis[maxn];
    int rk[maxn],val[maxn],f[maxn];
    bool vis[maxn];
    struct edge {
    	int nxt,to,w;
    } e[maxm<<1];
    struct Edge {
    	int u,v,w,h;
    	
    	bool operator < (const Edge &t) const {
    		return h>t.h;
    	}
    } E[maxm];
    struct node {
    	int v,w;
    	node() {}
    	node(int V,int W):v(V),w(W) {}
    	
    	bool operator < (const node &t) const {
    		return w>t.w;
    	}
    };
    priority_queue <node> q;
    vector <node> g[maxn];
    
    void addEdge(int u,int v,int w) {
    	e[++cnt].w=w;
    	e[cnt].to=v;
    	e[cnt].nxt=head[u];
    	head[u]=cnt;
    	e[++cnt].w=w;
    	e[cnt].to=u;
    	e[cnt].nxt=head[v];
    	head[v]=cnt;
    }
    
    void init() {
    	cnt=0;
    	for(int i=1;i<=n;++i)	
    		f[i]=i,head[i]=0,rk[i]=0;
    }
    
    void Dijkstra() {
    	fill(&dis[2],&dis[n]+1,2e9);
    	fill(&vis[1],&vis[n]+1,0);
    	q.push(node(1,0));
    	while(!q.empty()) {
    		node t=q.top(); q.pop();
    		if(vis[t.v] or (t.w^dis[t.v]))
    			continue;
    		vis[t.v]=1;
    		for(int i=head[t.v];i;i=e[i].nxt) {
    			int v=e[i].to;
    			if(vis[v]) continue;
    			if(dis[t.v]+e[i].w<dis[v]) {
    				dis[v]=dis[t.v]+e[i].w;
    				q.push(node(v,dis[v]));
    			}
    		}
    	}
    }
    
    int Find(int x) {
    	return x==f[x]?x:Find(f[x]);
    }
    
    void pushUp(int x,int k,int h) {
    	if(g[x][g[x].size()-1].w>k)	
    		g[x].push_back(node(h,k));
    }
    
    void merge(int x,int y,int h) {
    	x=Find(x),y=Find(y);
    	if(x==y) return;
    	if(rk[x]>rk[y]) swap(x,y);
    	else if(rk[x]==rk[y]) ++rk[y];
    	f[x]=y; val[x]=h;
    	pushUp(y,g[x][g[x].size()-1].w,h);
    }
    
    int calc(int x,int h) {
    	while((x^f[x]) and val[x]>h) x=f[x];
    	int l=0,r=g[x].size()-1,mid;
    	while(l<r) {
    		mid=l+r+1>>1;
    		if(g[x][mid].v>h)
    			l=mid;
    		else r=mid-1;
    	}
    	return g[x][l].w;
    }
    
    int main() {
    	for(int T=read(9);T;--T) {
    		n=read(9),m=read(9);
    		init();
    		for(int i=1;i<=m;++i) {
    			E[i]=(Edge){read(9),read(9),read(9),read(9)};
    			addEdge(E[i].u,E[i].v,E[i].w);
    		}
    		Dijkstra();
    		for(int i=1;i<=n;++i)
    			g[i].clear(),
    			g[i].push_back(node(2e9,dis[i]));
    		sort(E+1,E+m+1);
    		for(int i=1;i<=m;++i)
    			merge(E[i].u,E[i].v,E[i].h);
    		int q,k,s,lst=0,x,y;
    		q=read(9),k=read(9),s=read(9);
    		while(q--) {
    			x=(read(9)+lst*k-1)%n+1;
    			y=(read(9)+lst*k)%(s+1);
    			print(lst=calc(x,y),'
    ');
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    同余方程
    倒酒
    机器翻译
    vue 锚点定位
    解决vuex刷新页面数据丢失
    h5 input失去焦点软键盘把页面顶起
    js 监听ios手机键盘弹起和收起的事件
    js 将数组中的每一项安装奇偶重新组合成一个数组对象
    moment.js获取本周本月本年的开始日期和结束日期
    vue 所有的路由跳转加一个统一参数
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/15251521.html
Copyright © 2011-2022 走看看