[NOI2018]归程(可持久化并查集,Kruskal重构树)
解法一:
1.首先想到离线做法:将边和询问从大到小排序,并查集维护连通块以及每个连通块中所有点到1号点的最短距离。$O(n\log n)$
配合暴力等可以拿到75分。
2.很容易想到在线做法,使用可持久化并查集,询问时二分即可。
不能使用路径压缩,应该按秩合并,注意秩是树的深度而不是大小。$O((E+Q)\log^2 N)$
由于常数过大,基本过不去。
3.考虑优化算法二,发现访问历史版本并不需要修改而只需要询问,所以一开始只使用普通的并查集,用可持久化数组记录并查集的修改情况。
$O((N+E)\log N+Q\log^2 N)$,卡时通过。
4.算法三的复杂度已经难以优化,考虑优化常数。不需要可持久化数据结构,直接对并查集的每个点用vector存下修改情况,询问时二分即可。
$O(n\log^2 n)$,常数很小,轻松通过。
解法二:
考虑Kruskal重构树,对海拔跑一次Kruskal同时对每条边新建一个节点,权值为边的海拔,并对每个点存下重构树的子树中到1号点的最小值。
问题实际上是求一个点能通过走海拔不低于某个值的边到达的点中离1好号点最近的距离,也就是重构树上点权大于某个值的节点的子树中到1号点的最小值。倍增查询即可。
- #include<cstdio>
- #include<queue>
- #include<cstring>
- #include<algorithm>
- #define rep(i,l,r) for (int i=(l); i<=(r); i++)
- using namespace std;
- const int N=,inf=;
- bool b[N];
- int n,m,T,q,K,S,v,p,cnt,Ans,mn[N],fa[N],ans[N],to[N],nxt[N],dis[N],val[N],h[N];
- struct E{ int u,v,l,a; }e[N];
- struct Que{ int v,p,id; }que[N];
- struct P{ int x,d; };
- bool operator <(const P &a,const P &b){ return a.d>b.d; }
- priority_queue<P>Q;
- void add(int u,int v,int w){ to[++cnt]=v; val[cnt]=w; nxt[cnt]=h[u]; h[u]=cnt; }
- int get(int x){ return (fa[x]==x) ? x : fa[x]=get(fa[x]); }
- bool cmp(E a,E b){ return a.a>b.a; }
- bool cmp1(Que a,Que b){ return a.p>b.p; }
- void Dij(){
- rep(i,,n) dis[i]=inf,b[i]=; dis[]=;
- Q.push((P){,});
- while (!Q.empty()){
- int x=Q.top().x; Q.pop();
- if (b[x]) continue;
- b[x]=;
- for (int i=h[x],k; i; i=nxt[i])
- if (!b[k=to[i]] && dis[k]>dis[x]+val[i])
- Q.push((P){k,dis[k]=dis[x]+val[i]});
- }
- }
- int main(){
- freopen("return.in","r",stdin);
- freopen("return.out","w",stdout);
- for (scanf("%d",&T); T--; ){
- scanf("%d%d",&n,&m);
- rep(i,,n) h[i]=; cnt=; Ans=;
- rep(i,,m){
- scanf("%d %d %d %d",&e[i].u,&e[i].v,&e[i].l,&e[i].a);
- add(e[i].u,e[i].v,e[i].l); add(e[i].v,e[i].u,e[i].l);
- }
- sort(e+,e+m+,cmp); Dij();
- scanf("%d%d%d",&q,&K,&S);
- if (K){
- rep(i,,q){
- rep(i,,n) mn[i]=dis[i],fa[i]=i;
- scanf("%d%d",&v,&p);
- v=(v+K*Ans-)%n+; p=(p+K*Ans)%(S+); int st=;
- while (e[st].a>p && st<=m){
- int u=get(e[st].u),v=get(e[st].v);
- if (u!=v){ fa[u]=v; mn[v]=min(mn[v],mn[u]); }
- st++;
- }
- printf("%d\n",Ans=mn[get(v)]);
- }
- continue;
- }
- rep(i,,q) scanf("%d%d",&que[i].v,&que[i].p),que[i].id=i;
- sort(que+,que+q+,cmp1); int st=;
- rep(i,,n) mn[i]=dis[i],fa[i]=i;
- rep(i,,q){
- while (e[st].a>que[i].p && st<=m){
- int u=get(e[st].u),v=get(e[st].v);
- if (u!=v){ fa[u]=v; mn[v]=min(mn[v],mn[u]); }
- st++;
- }
- ans[que[i].id]=mn[get(que[i].v)];
- }
- rep(i,,q) printf("%d\n",ans[i]);
- }
- return ;
- }
75分
- #include<queue>
- #include<cstdio>
- #include<vector>
- #include<algorithm>
- #define pb push_back
- #define rep(i,l,r) for (int i=(l); i<=(r); i++)
- using namespace std;
- const int N=,inf=;
- bool b[N];
- int n,m,q,k,s,T,ans,cnt,dis[N],fa[N],mn[N],h[N],dep[N],to[N<<],val[N<<],nxt[N<<];
- struct E{ int u,v,l,a; }e[N];
- struct S{ int p,v; };
- struct P{ int x,d; };
- bool operator <(const E &a,const E &b){ return a.a>b.a; }
- bool operator <(const P &a,const P &b){ return a.d>b.d; }
- bool operator <(const S &a,const S &b){ return a.p>b.p; }
- vector<S>Fa[N],Mn[N];
- priority_queue<P>Q;
- void add(int u,int v,int w){ to[++cnt]=v; val[cnt]=w; nxt[cnt]=h[u]; h[u]=cnt; }
- int find(int x){ return (x==fa[x]) ? x : find(fa[x]); }
- void Dij(){
- rep(i,,n) dis[i]=inf,b[i]=; dis[]=; Q.push((P){,});
- while (!Q.empty()){
- int x=Q.top().x; Q.pop();
- if (b[x]) continue;
- b[x]=;
- for (int i=h[x],k; i; i=nxt[i])
- if (!b[k=to[i]] && dis[k]>dis[x]+val[i])
- Q.push((P){k,dis[k]=dis[x]+val[i]});
- }
- }
- void init(){
- cnt=; while (!Q.empty()) Q.pop();
- rep(i,,n) h[i]=,Fa[i].clear(),Mn[i].clear();
- }
- void work(){
- scanf("%d%d",&n,&m);
- rep(i,,m){
- scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].l,&e[i].a);
- add(e[i].u,e[i].v,e[i].l); add(e[i].v,e[i].u,e[i].l);
- }
- Dij(); sort(e+,e+m+);
- rep(i,,n) Fa[i].pb((S){inf,fa[i]=i}),Mn[i].pb((S){inf,mn[i]=dis[i]}),dep[i]=;
- rep(i,,m){
- int p=e[i].a,u=find(e[i].u),v=find(e[i].v);
- if (u==v) continue;
- if (dep[u]>dep[v]) swap(u,v);
- fa[u]=v; dep[v]=max(dep[u]+,dep[v]); mn[v]=min(mn[v],mn[u]);
- Fa[u].pb((S){p,fa[u]}); Mn[v].pb((S){p,mn[v]});
- }
- scanf("%d%d%d",&q,&k,&s); ans=;
- rep(i,,q){
- int v,p; scanf("%d%d",&v,&p); v=(v+k*ans-)%n+; p=(p+k*ans)%(s+);
- for (int f; v!=(f=(--lower_bound(Fa[v].begin(),Fa[v].end(),(S){p,}))->v); v=f);
- printf("%d\n",ans=(--lower_bound(Mn[v].begin(),Mn[v].end(),(S){p,}))->v);
- }
- }
- int main(){
- freopen("return.in","r",stdin);
- freopen("return.out","w",stdout);
- for (scanf("%d",&T); T--; ) init(),work();
- return ;
- }
解法一
- #include<queue>
- #include<cstdio>
- #include<cstring>
- #include<algorithm>
- #define rep(i,l,r) for (int i=(l); i<=(r); i++)
- using namespace std;
- const int N=,inf=;
- bool b[N];
- int n,m,q,K,v,p,S,T,ans,tot,cnt,dis[N],fa[N],s[N],mn[N],h[N],Fa[N][],to[N<<],val[N<<],nxt[N<<];
- struct E{ int u,v,l,a; }e[N];
- struct P{ int x,d; };
- bool operator <(const P &a,const P &b){ return a.d>b.d; }
- bool operator <(const E &a,const E &b){ return a.a>b.a; }
- priority_queue<P>Q;
- void add(int u,int v,int w){ to[++cnt]=v; val[cnt]=w; nxt[cnt]=h[u]; h[u]=cnt; }
- int find(int x){ return (x==fa[x]) ? x : fa[x]=find(fa[x]); }
- void Dij(){
- rep(i,,n) dis[i]=inf,b[i]=; dis[]=; Q.push((P){,});
- while (!Q.empty()){
- int x=Q.top().x; Q.pop();
- if (b[x]) continue;
- b[x]=;
- for (int i=h[x],k; i; i=nxt[i])
- if (!b[k=to[i]] && dis[k]>dis[x]+val[i])
- Q.push((P){k,dis[k]=dis[x]+val[i]});
- }
- }
- void dfs(int x){
- if (x>n) mn[x]=inf; else mn[x]=dis[x];
- for (int i=h[x],k; i; i=nxt[i])
- Fa[k=to[i]][]=x,dfs(k),mn[x]=min(mn[x],mn[k]);
- }
- void Kruskal(){
- rep(i,,n) h[i]=; cnt=; tot=n;
- rep(i,,n) fa[i]=i;
- rep(i,,m){
- int u=find(e[i].u),v=find(e[i].v);
- if (u==v) continue;
- s[++tot]=e[i].a; fa[u]=fa[v]=fa[tot]=tot;
- add(tot,u,); add(tot,v,);
- }
- dfs(tot);
- }
- void work(){
- scanf("%d%d",&n,&m);
- rep(i,,m){
- scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].l,&e[i].a);
- add(e[i].u,e[i].v,e[i].l); add(e[i].v,e[i].u,e[i].l);
- }
- Dij(); sort(e+,e+m+); Kruskal();
- rep(i,,) rep(x,,tot) Fa[x][i]=Fa[Fa[x][i-]][i-];
- scanf("%d%d%d",&q,&K,&S); ans=;
- while (q--){
- scanf("%d%d",&v,&p);
- v=(v+K*ans-)%n+; p=(p+K*ans)%(S+);
- for (int i=; ~i; i--) if (Fa[v][i] && s[Fa[v][i]]>p) v=Fa[v][i];
- printf("%d\n",ans=mn[v]);
- }
- }
- void init(){ cnt=; memset(Fa,,sizeof(Fa)); memset(h,,sizeof(h)); }
- int main(){
- freopen("return.in","r",stdin);
- freopen("return.out","w",stdout);
- for (scanf("%d",&T); T--; ) init(),work();
- return ;
- }
解法二
[NOI2018]归程(可持久化并查集,Kruskal重构树)的更多相关文章
- 洛谷P4768 [NOI2018]归程 [可持久化并查集,Dijkstra]
题目传送门 归程 格式难调,题面就不放了. 分析: 之前同步赛的时候反正就一脸懵逼,然后场场暴力大战,现在呢,还是不会$Kruskal$重构树,于是就拿可持久化并查集做. 但是之前做可持久化并查集的时 ...
- [NOI2018] 归程 可持久化并查集
题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个n 个节点.m 条边的无向连通图(节点的编号从 1至 n).我们依次用 l,a描述一条边的长度.海拔. ...
- BZOJ5415:[NOI2018]归程(可持久化并查集,最短路)
Description Input Output Sample Input1 14 31 2 50 12 3 100 23 4 50 15 0 23 02 14 13 13 2 Sample Outp ...
- UOJ 393 【NOI2018】归程——可持久化并查集
题目:http://uoj.ac/problem/393 题解:https://www.cnblogs.com/HocRiser/p/9368067.html 但过不了 UOJ 的 hack 数据.不 ...
- 洛谷P4768 [NOI2018]归程(可持久化并查集,最短路)
闲话 一个蒟蒻,在网络同步赛上进行了这样的表演-- T2组合计数不会,T3字符串数据结构不会,于是爆肝T1 一开始以为整个地图都有车,然后写了2h+的树套树,终于发现样例过不去 然后写可持久化并查集D ...
- NOI2018Day1T1 归程 并查集 kruskal kruskal重构树 倍增表 Dijkstra
原文链接https://www.cnblogs.com/zhouzhendong/p/NOI2018Day1T1.html 题目传送门 - 洛谷P4768 题意 给定一个无向连通图,有 $n$ 个点 ...
- BZOJ5415[Noi2018]归程——kruskal重构树+倍增+堆优化dijkstra
题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 n 个节点.m 条边的无向连通图(节点的编号从 1 至 n).我们依次用 l,a 描述一条边的长度.海 ...
- [学习笔记]kruskal重构树 && 并查集重构树
Kruskal 重构树 [您有新的未分配科技点][BZOJ3545&BZOJ3551]克鲁斯卡尔重构树 kruskal是一个性质优秀的算法 加入的边是越来越劣的 科学家们借这个特点尝试搞一点事 ...
- [洛谷P4768] [NOI2018]归程 (kruskal重构树模板讲解)
洛谷题目链接:[NOI2018]归程 因为题面复制过来有点炸格式,所以要看题目就点一下链接吧\(qwq\) 题意: 在一张无向图上,每一条边都有一个长度和海拔高度,小\(Y\)的家在\(1\)节点,并 ...
随机推荐
- JQuery实现锚点平滑滚动
一般使用锚点来跳转到页面指定位置的时候,会生硬地立即跳转到指定位置,但是有些时候我们想要平滑地过渡到指定的位置,那么可以使用JQuery简单的实现这个效果: 比如,这里我们将通过点击<a> ...
- Git-标签管理【转】
本文转载自:http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 标签管理 发布一个版本 ...
- Java8 Lambda
Demo: package com.qhong; public class Main { public static void main(String[] args) throws Exception ...
- RPC框架yar安装
官方网站: http://pecl.php.net/package/msgpack http://pecl.php.net/package/yar 先安装 msgpack $ git clone ht ...
- POJ 1239 Increasing Sequences(经典的两次dp)
http://poj.org/problem?id=1239 题意:给出一串序列,现在要添加逗号作为分隔符,使得序列是递增序列,然后让最后一个数尽量小,第一个数尽量大. 思路:先从头到尾进行一次dp, ...
- 51Nod 1070 Bash游戏 V4(斐波那契博弈)
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1070 题意: 思路: 这个是斐波那契博弈,http://blog.csd ...
- Codeforces Round #307 (Div. 2) D. GukiZ and Binary Operations 矩阵快速幂优化dp
D. GukiZ and Binary Operations time limit per test 1 second memory limit per test 256 megabytes inpu ...
- mysql获取随机数据的方法
order by rand() 数据多了极慢,随机性非常好,适合非常小数据量的情况. 复制代码 代码如下: SELECT * FROM table_name AS r1 JOIN (SELECT (R ...
- Android------视频播放器(包含全屏播放,快退,快进,腾讯新闻的列表播放等)
前段时间做了一个新闻APP,涉及到了列表视频播放,和腾讯新闻APP差不多,总结了一下代码,写了一个Demo来分享给大家. 用了 TabLayout+RecylerView+自定义视频控件 完成的 ...
- centos7(debian,manjora,freebsd)命令及安装mysql、git、gpg、gogs,安装docker,zsh,chrome
最小安装: 1. 选择English 2. DATE & TIME 修改好本地时间 SOFTWARE SELECTION默认的Minimal Install就好 INSTALLATION DE ...