解法一:

  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重构树)的更多相关文章

  1. 洛谷P4768 [NOI2018]归程 [可持久化并查集,Dijkstra]

    题目传送门 归程 格式难调,题面就不放了. 分析: 之前同步赛的时候反正就一脸懵逼,然后场场暴力大战,现在呢,还是不会$Kruskal$重构树,于是就拿可持久化并查集做. 但是之前做可持久化并查集的时 ...

  2. [NOI2018] 归程 可持久化并查集

    题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个n 个节点.m 条边的无向连通图(节点的编号从 1至 n).我们依次用 l,a描述一条边的长度.海拔. ...

  3. 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 ...

  4. UOJ 393 【NOI2018】归程——可持久化并查集

    题目:http://uoj.ac/problem/393 题解:https://www.cnblogs.com/HocRiser/p/9368067.html 但过不了 UOJ 的 hack 数据.不 ...

  5. 洛谷P4768 [NOI2018]归程(可持久化并查集,最短路)

    闲话 一个蒟蒻,在网络同步赛上进行了这样的表演-- T2组合计数不会,T3字符串数据结构不会,于是爆肝T1 一开始以为整个地图都有车,然后写了2h+的树套树,终于发现样例过不去 然后写可持久化并查集D ...

  6. NOI2018Day1T1 归程 并查集 kruskal kruskal重构树 倍增表 Dijkstra

    原文链接https://www.cnblogs.com/zhouzhendong/p/NOI2018Day1T1.html 题目传送门 - 洛谷P4768 题意 给定一个无向连通图,有 $n$ 个点 ...

  7. BZOJ5415[Noi2018]归程——kruskal重构树+倍增+堆优化dijkstra

    题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 n 个节点.m 条边的无向连通图(节点的编号从 1 至 n).我们依次用 l,a 描述一条边的长度.海 ...

  8. [学习笔记]kruskal重构树 && 并查集重构树

    Kruskal 重构树 [您有新的未分配科技点][BZOJ3545&BZOJ3551]克鲁斯卡尔重构树 kruskal是一个性质优秀的算法 加入的边是越来越劣的 科学家们借这个特点尝试搞一点事 ...

  9. [洛谷P4768] [NOI2018]归程 (kruskal重构树模板讲解)

    洛谷题目链接:[NOI2018]归程 因为题面复制过来有点炸格式,所以要看题目就点一下链接吧\(qwq\) 题意: 在一张无向图上,每一条边都有一个长度和海拔高度,小\(Y\)的家在\(1\)节点,并 ...

随机推荐

  1. php5.4一下 json_encode 不转义中文

    转载地址:http://www.nowamagic.net/php/php_FunctionJsonEncode.php 在 php 中使用 json_encode() 内置函数(php > 5 ...

  2. oracle数据库中导入Excel表格中的数据

    1.点击[工具]-->[ODBC 导入器],如图: 2.在导入器里选择第一个[来自ODBC的数据],用户名/系统DSN-->填写[Excel Files],输入用户名和密码,点击 [连接] ...

  3. oracle 11g 数据库中报:协议适配器错误

    本人遇到该问题,到数据库服务器上重新启动监听和实例就OK了.

  4. 【javascript】浏览器用户代理检测脚本实现

    以下是完整的用户代理字符串检测脚本,包括检测呈现引擎.平台.Windows操作系统.移动设备和游戏系统. var client = function(){ // 呈现引擎 var engine = { ...

  5. awk根据指定的字符串分割字符串

    以从字符串"hello-kitty-red-for-you"中获取-for前面的内容为例: echo "hello-kitty-red-for-you" |aw ...

  6. [参考]用递归的方法获取 字符 对应的 二进制字符串 (C/C++)

    将字符转换为16进制字符串.十进制字符串可以参考这里:https://www.cnblogs.com/stxs/p/8846545.html 代码及调试结果 举例:字符'a',查ASCII码表它对应的 ...

  7. Redis之字符串

    Redis 字符串 String 是redis最基本的类型,value 不仅可以是 String,也可以是数字. 使用 Strings 类型,可以完全实现目前 Memcached 的功能,并且效率更高 ...

  8. UOJ #266 【清华集训2016】 Alice和Bob又在玩游戏

    题目链接:Alice和Bob又在玩游戏 这道题就是一个很显然的公平游戏. 首先\(O(n^2)\)的算法非常好写.暴力枚举每个后继计算\(mex\)即可.注意计算后继的时候可以直接从父亲转移过来,没必 ...

  9. 在 php 中使用 strace、gdb、tcpdump 调试工具

    转自:http://www.syyong.com/php/Using-strace-GDB-and-tcpdump-debugging-tools-in-PHP.html 在 php 中我们最常使用调 ...

  10. vapply

    尽管 sapply 非常方便和智能,但有时智能可能隐藏着风险.假如我们有一个数字列表:x <- list(c(1, 2), c(2, 3), c(1, 3))如果我们想得到一个向量,其中每个元素 ...