洛谷 P4197 Peaks

题意:

有 nnn 个山峰,每一个山峰高 hih_ihi​ ,有 mmm 条双向带权边将一些山峰连接起来,有 qqq 次询问,每次询问 (v,x,k)(v,x,k)(v,x,k) ,即从 vvv 山峰出发经过边权不超过 xxx 的边能到的点里的第 kkk 高的山峰。

思路:

考虑在线的解法,要求经过边权不超过 xxx 的边能到的点,所以就想到使用 kruskalkruskalkruskal 重构树,那么每一个山峰就是叶子节点,使用 dfsdfsdfs 序,那么每一个点都有一个区间,而 kruskalkruskalkruskal 重构树是一个大根堆,要求不超过 xxx 的边权,即找到深度的最小的点且点权小于等于 xxx ,那么在该点对应区间上求区间第 kkk 大即可

代码:

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
struct edge{int from,to,w;edge(int a,int b,int c){from=a,to=b,w=c;}bool operator < (const edge &t)const {return w<t.w;}
};
vector<edge>e,E[N];
vector<int>v;
int in[N],out[N],mx;
int val[N],w[N],fa[N],f[N][22];
int getid(int x){return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}
int fi(int x){if(x==fa[x])return x;return fa[x]=fi(fa[x]);}
int n,m,q,tot,Time;
//zx_tree
struct zx_tree{int ls[N*50],rs[N*50],rt[N*50],tot=0,sum[N*50];inline void update(int &root,int pre,int l,int r,int pos){root=++tot;ls[root]=ls[pre],rs[root]=rs[pre],sum[root]=sum[pre]+1;if(l==r)return ;int mid=(l+r)/2;if(pos<=mid)update(ls[root],ls[pre],l,mid,pos);else update(rs[root],rs[pre],mid+1,r,pos);}inline int query(int root,int pre,int l,int r,int k){//区间第k小if(l==r)return l;int mid=(l+r)/2;int Sum=sum[ls[root]]-sum[ls[pre]];if(Sum>k)return query(ls[root],ls[pre],l,mid,k);else return query(rs[root],rs[pre],mid+1,r,k-Sum);}
}T;
void cl(int x){for(int i=1;(1<<i)<=n;i++){f[x][i]=f[f[x][i-1]][i-1];}
}
void dfs(int x){cl(x);in[x]=++Time;if(x<=n)T.update(T.rt[Time],T.rt[Time-1],1,mx,getid(val[x]));//利用时间戳来建树else T.rt[Time]=T.rt[Time-1];for(edge now:E[x]){int v=now.to;dfs(v);}out[x]=Time;
}
int main()
{scanf("%d%d%d",&n,&m,&q);for(int i=1;i<=n;i++){scanf("%d",&val[i]);v.push_back(val[i]);}for(int i=1;i<=2*n;i++)fa[i]=i;sort(v.begin(),v.end());v.erase(unique(v.begin(),v.end()),v.end());mx=v.size();for(int i=1,u,v,w;i<=m;i++){scanf("%d%d%d",&u,&v,&w);e.push_back(edge(u,v,w));}sort(e.begin(),e.end());tot=n;for(edge now:e){int f1=fi(now.from),f2=fi(now.to);if(f1==f2)continue;++tot;w[tot]=now.w;f[f1][0]=f[f2][0]=tot;fa[f1]=fa[f2]=tot;E[tot].push_back(edge(0,f1,0));E[tot].push_back(edge(0,f2,0));if(tot==2*n-1)break;}for(int i=1;i<=tot;i++)if(!in[i])dfs(fi(i));while(q--){int a,b,c;scanf("%d%d%d",&a,&b,&c);for(int i=18;~i;i--){if(f[a][i]&&w[f[a][i]]<=b)a=f[a][i];}int L=T.rt[in[a]-1],R=T.rt[out[a]];int Max=T.sum[R]-T.sum[L];if(Max<c){puts("-1");continue;}int pos=T.query(R,L,1,mx,Max-c);//第k大转化为第k小printf("%d\n",v[pos-1]);}
}

Comet OJ - Contest #11 D.isaster

题意:

对一张 nnn 个点 mmm 条边点带权的无向连通图进行以下两种操作:
1.修改点 xxx 的点权。
2.询问从点 xxx 出发只经过编号不大于 yyy 的点能到达的所有点的点权之积取模 998244353998244353998244353 。

思路:

和上一题类似,只要将kruskal重构树求出来,在利用线段树维护每一个点的点权积即可,支持单点修改。
(不知道为什么用c++就一直RE,改了两个小时,用c++17就过了)

代码:

#include <bits/stdc++.h>
#define ll long long
#define ls x<<1
#define rs x<<1|1
using namespace std;
const int N=1e6+10;
const int mod=998244353;
int n,m,q,op,x,y;
int val[N],fa[N],f[N][22],arr[N];
int fi(int x){if(x==fa[x])return x;return fa[x]=fi(fa[x]);
}
struct edge{int from,to,w;edge(int a,int b,int c){from=a,to=b,w=c;}bool operator < (const edge &t)const {return w<t.w;}
};
vector<edge>e,E[N];//原边和新构的二叉树,一个大根堆
void kurskal(){for(edge now:e){//int f1=fi(now.from),f2=fi(now.to);if(f1==f2)continue;if(f1<f2)swap(f1,f2);fa[f2]=f1;E[f1].push_back(edge(0,f2,0));//加入新边}
}
int Time,in[N],out[N],id[N];
void dfs(int x,int Fa){f[x][0]=Fa;for(int i=1;i<20;i++)f[x][i]=f[f[x][i-1]][i-1];in[x]=++Time;id[Time]=x;for(edge now:E[x]){int v=now.to;dfs(v,x);}out[x]=Time;
}
//
struct node{int l,r;ll sum;
}T[N*4];
void up(int x){T[x].sum=(T[ls].sum*T[rs].sum)%mod;
}
void built(int x,int l,int r){T[x].l=l;T[x].r=r;if(l==r){T[x].sum=val[id[l]];return ;}int mid=(l+r)/2;built(ls,l,mid);built(rs,mid+1,r);up(x);
}
void add(int x,int pos,int val){if(T[x].l==T[x].r){T[x].sum=val%mod;return ;}int mid=(T[x].l+T[x].r)/2;if(pos<=mid)add(ls,pos,val);else add(rs,pos,val);up(x);
}
ll query(int x,int LL,int RR){if(T[x].l>=LL&&T[x].r<=RR){return T[x].sum%mod;}int mid=(T[x].l+T[x].r)/2;ll ans=1;if(LL<=mid)ans=(ans*query(ls,LL,RR))%mod;if(RR>mid)ans=(ans*query(rs,LL,RR))%mod;return ans;
}
//int main()
{scanf("%d%d%d",&n,&m,&q);for(int i=1;i<=n;i++)fa[i]=i;for(int i=1;i<=n;i++)scanf("%d",&val[i]),val[i]%=mod;for(int i=1,u,v;i<=m;i++){scanf("%d%d",&u,&v);//if(u==v)continue;e.push_back(edge(u,v,max(u,v)));}sort(e.begin(),e.end());kurskal();dfs(n,0);built(1,1,Time);while(q--){scanf("%d%d%d",&op,&x,&y);if(op==1){if(x>y){puts("0");continue;}for(int i=19;~i;i--){if(f[x][i]&&f[x][i]<=y)x=f[x][i];}int L=in[x],R=out[x];printf("%lld\n",query(1,L,R));}else{add(1,in[x],y);}}
}

洛谷 P4768 [NOI2018]归程

题意:

给一个无向图,每一条边有一个长度和海拔,结点 111 是终点,多次询问,每次询问从 vvv 点出发要到终点,给定一个 kkk 值,对于经过的每一条边,如果海拔大于等于 kkk 可以使用车,如果小于则只能步行,在每一个结点可以随时下车步行,但不能再次上车,问到终点的最小步行长度。

思路:

由于是大于等于p,故我们在建重构树的时候使用最大生成树的建法,使其成为一个小根堆,那么对于每次询问,和上面一样我们倍增找到一个结点,可以知道该结点形成的子树都可以使用车,那么步行的最短距离就是子树的点中与终点的最短距离,可以预处理出来。

代码:

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=4e5+10;
int n,m,T,ans,q,k,s,v,p,dis[N],w[N],v0,p0;
struct edge{int from,to,w;edge(int a,int b,int c){from=a,to=b,w=c;}bool operator < (const edge &t)const {return w>t.w;}
};
int f[N][22],fa[N];
int fi(int x){if(x==fa[x])return x;return fa[x]=fi(fa[x]);
}
vector<edge>e,E[N],E1[N];
int tot;
void kurskal(){for(edge now:e){int f1=fi(now.from),f2=fi(now.to);if(f1==f2)continue;++tot;w[tot]=now.w;f[f1][0]=f[f2][0]=tot;fa[f1]=fa[f2]=tot;E[tot].push_back(edge(0,f1,0));E[tot].push_back(edge(0,f2,0));//加入新边if(tot==2*n-1)break;}
}
void init(){tot=n;e.clear();ans=0;for(int i=1;i<=n;i++)w[i]=inf;for(int i=1;i<=2*n;i++)fa[i]=i,E[i].clear(),E1[i].clear();for(int i=1;i<=2*n;i++)for(int j=0;j<=20;j++)f[i][j]=0;}
void change(){v=(v0+k*ans-1)%n+1;p=(p0+k*ans)%(s+1);
}
bool vis[N];
int val[N];
void dfs(int x){vis[x]=1;for(int i=1;i<=20;i++)f[x][i]=f[f[x][i-1]][i-1];val[x]=dis[x];//cout<<"edge:"<<x<<" "<<val[x]<<endl;for(edge now:E[x]){int v=now.to;//cout<<"next:"<<v<<endl;dfs(v);val[x]=min(val[x],val[v]);}
}
void dij(){memset(dis,inf,sizeof(dis));memset(vis,0,sizeof(vis));priority_queue<pair<int,int> >q;q.push(make_pair(0,1));dis[1]=0;while(!q.empty()){int now=q.top().second;q.pop();if(vis[now])continue;vis[now]=1;for(edge nnow:E1[now]){int v=nnow.to;if(dis[v]>dis[now]+nnow.w){dis[v]=dis[now]+nnow.w;q.push(make_pair(-dis[v],v));}}}//for(int i=1;i<=2*n;i++)cout<<dis[i]<<endl;
}
int main()
{scanf("%d",&T);while(T--){scanf("%d%d",&n,&m);init();for(int i=1,u,v,x,y;i<=m;i++){scanf("%d%d%d%d",&u,&v,&x,&y);e.push_back(edge(u,v,y));E1[u].push_back(edge(0,v,x));E1[v].push_back(edge(0,u,x));}sort(e.begin(),e.end());kurskal();dij();memset(vis,0,sizeof(vis));for(int i=1;i<=tot;i++)if(!vis[i])dfs(fi(i));//for(int i=1;i<=tot;i++)cout<<val[i]<<endl;scanf("%d%d%d",&q,&k,&s);while(q--){scanf("%d%d",&v0,&p0);change();for(int i=19;~i;i--){if(f[v][i]&&w[f[v][i]]>p)v=f[v][i];}//cout<<v<<endl;printf("%d\n",val[v]);ans=val[v];}}
}

2016 ACM-ICPC CHINA-Final G. Pandaria

题意:

给一个树,每条边有边权,每个点有颜色,多次询问,强制在线,每次询问从 vvv 点出发,经过边权不超过 qqq 的边能到的点里,出现最多次的颜色。

思路:

学习了 kruskalkruskalkruskal 重构树后这题就比较简单了,首先利用重构树得到能到达的点集,问题就是如何维护出现最多次的颜色,我们可以先不重构树,而是对每一个点建立一个权值线段树,在重构的时候合并线段树,那么合并后的结点信息就是答案,在回答时先倍增到目标结点,然后利用线段树查询。(细节较多,注意初始化)

代码:

#include <bits/stdc++.h>
#define ls e[x].l
#define rs e[x].r
using namespace std;
const int N=2e5+10;
int n,T,m,q,c[N];
struct node{//权值线段树int l,r,sum,id;
}e[N*50];
void up(int x){e[x].sum=max(e[ls].sum,e[rs].sum);if(e[ls].sum==e[x].sum)e[x].id=e[ls].id;//取较小的else e[x].id=e[rs].id;
}
int cnt,tot,rt[N*50];//开50倍
void update(int &x,int l,int r,int pos){if(!x)x=++cnt;if(l==r){e[x].sum++;e[x].id=l;return ;}int mid=(l+r)/2;if(pos<=mid)update(ls,l,mid,pos);else update(rs,mid+1,r,pos);up(x);
}
int merge(int x,int y,int l,int r){if(!x||!y)return x+y;if(l==r){e[x].sum+=e[y].sum;e[x].id=l;return x;}int mid=(l+r)/2;ls=merge(e[x].l,e[y].l,l,mid);rs=merge(e[x].r,e[y].r,mid+1,r);up(x);return x;
}
int ans[N];
int fa[N];
int fi(int x){if(x==fa[x])return x;return fa[x]=fi(fa[x]);
}
struct edge{int from,to,w;edge(int a=0,int b=0,int c=0){from=a,to=b,w=c;}bool operator < (const edge &t)const{return w<t.w;}
}E[N*2];
bool vis[N];
int son[N][2],f[N][22],w[N];
void kruskal(){sort(E+1,E+m+1);for(int i=1;i<=m;i++){int f1=fi(E[i].from),f2=fi(E[i].to);if(f1==f2)continue;++tot;w[tot]=E[i].w;rt[tot]=merge(rt[f1],rt[f2],1,n);//将合并的结点给新节点赋值son[tot][0]=f1,son[tot][1]=f2;f[f1][0]=f[f2][0]=tot;fa[f2]=fa[f1]=tot;ans[tot]=e[rt[tot]].id;//存储答案}
}
void dfs(int u){vis[u]=1;for(int i=1;i<=20;i++)f[u][i]=f[f[u][i-1]][i-1];//倍增if(son[u][0])dfs(son[u][0]);if(son[u][1])dfs(son[u][1]);
}
int Ans;
void init(){//Ans=0;for(int i=1;i<=2*n;i++)rt[i]=0,vis[i]=0;for(int i=1;i<=cnt;i++)e[i].l=e[i].r=e[i].sum=e[i].id=0;for(int i=1;i<=2*n;i++)fa[i]=i,son[i][0]=son[i][1]=0;//很关键,否则TLEfor(int i=1;i<=2*n;i++)for(int j=1;j<=20;j++)f[i][j]=0;tot=n;cnt=0;
}
int main()
{scanf("%d",&T);int ca=1;while(T--){scanf("%d%d",&n,&m);init();printf("Case #%d:\n",ca++);for(int i=1;i<=n;i++)scanf("%d",&c[i]),update(rt[i],1,n,c[i]);for(int i=1,u,v,w;i<=m;i++){scanf("%d%d%d",&u,&v,&w);E[i]=edge(u,v,w);}for(int i=1;i<=n;i++)ans[i]=c[i];kruskal();for(int i=1;i<=tot;i++)if(!vis[i])dfs(fi(i));scanf("%d",&q);while(q--){int x,W;scanf("%d%d",&x,&W);x^=Ans;W^=Ans;for(int i=19;~i;i--){if(f[x][i]&&w[f[x][i]]<=W)x=f[x][i];}//cout<<x<<endl;printf("%d\n",ans[x]);Ans=ans[x];}}
}

kruskal重构树练习相关推荐

  1. 洛谷P4768 [NOI2018]归程(Kruskal重构树)

    题意 直接看题目吧,不好描述 Sol 考虑暴力做法 首先预处理出从$1$到每个节点的最短路, 对于每次询问,暴力的从这个点BFS,从能走到的点里面取$min$ 考虑如何优化,这里要用到Kruskal重 ...

  2. 【NOI 2018】归程(Kruskal重构树)

    题面在这里就不放了. 同步赛在做这个题的时候,心里有点纠结,很容易想到离线的做法,将边和询问一起按水位线排序,模拟水位下降,维护当前的各个联通块中距离$1$最近的距离,每次遇到询问时输出所在联通块的信 ...

  3. kruskal 重构树(讲解 + 例题)

    kruskal重构树 如何建树 模仿kruskalkruskalkruskal,先将所有边排序. 依次遍历每一条边,如果这条边的两个节点(u,vu, vu,v)不在同一个连通块里面, 则新建一个nod ...

  4. Network 黑暗爆炸 - 3732 倍增lca || Kruskal重构树

    传送门 文章目录 题意: 思路: 题意: 思路: 两点间最长边最小值一定是最小生成树上两点间的最大值,这个比较容易证,就不多说了. 知道这个结论后, 我们直接跑一个KruskalKruskalKrus ...

  5. [ONTAK2010] Peaks加强版 (kruskal重构树+主席树+倍增)

    Peaks description solution code description 在Bytemountains有N座山峰,每座山峰有他的高度h_i 有些山峰之间有双向道路相连,共M条路径,每条路 ...

  6. [NOI2018] 归程(线段树维护并查集的可持久化/kruskal重构树,倍增+dijkstra最短路)

    [NOI2018] 归程 description solution1 code1 solution2 code description 题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要 ...

  7. AGC002(D~F)【Kruskal重构树,博弈论,dp】

    正题 AT1998 [AGC002D] Stamp Rally[Kruskal重构树,倍增] https://www.luogu.com.cn/problem/AT1998 题目大意 给出nnn个点m ...

  8. 【kruskal重构树】【主席树】werewolf 狼人(P4899)

    正题 P4899 题目大意 给你一个图,对于每次询问Si,Ei,Li,RiS_i,E_i,L_i,R_iSi​,Ei​,Li​,Ri​,回答从SiS_iSi​走到EiE_iEi​,是否存在路径满足前面 ...

  9. LOJ.2865.[IOI2018]狼人(Kruskal重构树 主席树)

    LOJ 洛谷 这题不就是Peaks(加强版)或者归程么..这算是\(IOI2018\)撞上\(NOI2018\)的题了? \(Kruskal\)重构树(具体是所有点按从小到大/从大到小的顺序,依次加入 ...

  10. LOJ.2718.[NOI2018]归程(Kruskal重构树 倍增)

    LOJ2718 BZOJ5415 洛谷P4768 Rank3+Rank1无压力 BZOJ最初还不是一道权限题... Update 2019.1.5 UOJ上被hack了....好像是纯一条链的数据过不 ...

最新文章

  1. Linear Regression(一)——
  2. 数据库执行计划慢导致I/O 慢
  3. 为什么EXE不能超过4GB
  4. [网络收集]avascript中top.location.href 与 location.href的区别
  5. 【Linux高频命令专题(23)】tar
  6. 《面向模式的软件体系结构2-用于并发和网络化对象模式》读书笔记(13)--- 线程安全接口和双检查加锁优化...
  7. python整数转换字符串_Python | 将字符串转换为整数列表
  8. java音频文件怎么打开_java 怎么读取音乐文件
  9. linux 下执行python.py 无效解决方案
  10. 浅议PIM(一文看懂PIM)
  11. 五大常用算法之回溯法
  12. windows98 java_在win98下安装JSP环境
  13. CDN是什么?CDN的工作原理?使用CDN有什么优势?
  14. 基于ESP8266芯片的实时温湿度传感器
  15. 数学系列 (二)自然数、分数、小数、算数、代数
  16. Hadoop学习(二)---Secondary结点的配置以及HDFS的常用命令以及API的使用
  17. Purdue, Mathematics Area Examination 学习笔记(1)
  18. 用计算机时的注意事项,计算机使用注意事项
  19. 智能车竞赛技术报告 | 全向行进组 - 东北林业大学- 进取号E
  20. python中常用的转义字符

热门文章

  1. tf.argmax()的详细用法
  2. mysql存储过程fetch into_存储过程fetch into
  3. JavaScript 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
  4. App 启动时间优化方法详解
  5. OpenWrt 概述与快速入门
  6. 图像目标区域质心计算
  7. 全球与中国电磁波吸收材料市场发展态势与前景策略分析报告2022-2028年
  8. meshing:The mesh file exporter could not resolve cyclic dependencies in overlapping contact regions.
  9. 脚本实现监控APP的专项数据:CPU 内存 电量 流量
  10. [转载]彻底理解JAVA动态代理