NOI2018 D1T1 洛谷P4768 归程 (Kruskal重构树)

实际上是一个最短路问题,但加上了海拔这个条件限制,要在海拔<水位线p中找最短路。

这里使用Kruskal重构树,将其按海拔建成小根堆,我们就可以在树中用倍增找出他不得不下车的点;树中节点有两个权值L(最短路)和a(海拔),找到我们想要的a,此时的L就是答案。

来看一下总的算法分析吧......

先按海拔a从高到低排序,然后构建Kruskal重构树,按海拔每次选出剩余边中海拔最高的一条边插入到树中,建成一个小根堆。

接下来考虑询问——

对于一个水位线p:

(1)树中点x的海拔大于p,那么在x的子树中,不需要下车(因为是个小根堆嘛),对答案没有贡献。

(2)我们找到了x节点的海拔小于p,那么他就要在x点下车,对答案的贡献就是x到1的距离,两个子问题:用dijkstra预处理1到各点的最短路;用倍增找到x节点。

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define INF  0x3f3f3f3f
  4 inline int read(){
  5     int x=0;
  6     char ch=getchar();
  7     while(ch<'0'||ch>'9') ch=getchar();
  8     while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
  9     return x;
 10 }
 11 const int N=4e5+10;
 12 int fa[N][20],dep[N];
 13 int T,n,m,last;
 14 struct node{
 15     int u,v,l,a,nxt;
 16     bool operator<(const node &b) const{return a>b.a;} //小根堆 
 17 }e[N<<1],tmp[N<<1],edge[N<<1];
 18 int head[N],tot,dis[N];
 19 struct heap{
 20     int x,dis;
 21     bool operator<(const heap&b) const {return dis>b.dis;}//小根堆 
 22 };
 23 int f[N],cnt;
 24  
 25 inline void Add(int x,int y,int z){
 26     edge[++tot].v=y,edge[tot].l=z,edge[tot].nxt=head[x];
 27     head[x]=tot;
 28 }
 29  
 30 inline int find(int x){
 31     return f[x]==x?x:f[x]=find(f[x]);
 32 }
 33  
 34 inline void add(int x,int y){
 35     edge[++tot].v=y,edge[tot].nxt=head[x];
 36     head[x]=tot;
 37 }
 38  
 39 inline void init(){
 40     memset(head,0,sizeof(head));
 41     memset(fa,0,sizeof(fa));
 42     memset(f,0,sizeof(f));
 43     memset(tmp,0,sizeof(tmp));
 44     memset(edge,0,sizeof(edge));
 45     last=tot=0;
 46 }
 47  
 48 inline void dijkstra(){//求出从1到各点的最短路 
 49     priority_queue<heap> q;
 50     memset(dis,0x3f,sizeof(dis));
 51     dis[1]=0;
 52     q.push((heap){1,0});
 53     while(!q.empty()){
 54         heap now=q.top();
 55         q.pop();
 56         int x=now.x;
 57         if(dis[x]<now.dis) continue;//剪枝 
 58         for(int i=head[x];i;i=edge[i].nxt){
 59             int y=edge[i].v;
 60             if(dis[y]>dis[x]+edge[i].l){
 61                 dis[y]=dis[x]+edge[i].l;
 62                 q.push((heap){y,dis[y]});
 63             }
 64         }
 65     }
 66 }
 67  
 68 inline void kruskal(){
 69     sort(e+1,e+m+1);
 70     for(int i=1;i<=n;i++) f[i]=i;
 71     cnt=n;
 72     int num=0;//统计边数 
 73     for(int i=1;i<=m;i++){
 74         int fu=find(e[i].u),fv=find(e[i].v);
 75         if(fu!=fv){
 76             num++;
 77             tmp[++cnt].a=e[i].a;//海拔 
 78             f[fu]=f[fv]=f[cnt]=cnt;
 79             add(cnt,fu),add(cnt,fv);//父亲向孩子连边 
 80         }
 81         if(num==n-1) break;
 82     }
 83 }
 84  
 85 inline void dfs(int x,int p){
 86     dep[x]=dep[p]+1,fa[x][0]=p;
 87     for(int i=1;i<=19;i++)//dfs过程中处理ST 
 88         fa[x][i]=fa[fa[x][i-1]][i-1];
 89     for(int i=head[x];i;i=edge[i].nxt){
 90         int y=edge[i].v;
 91         dfs(y,x);
 92         tmp[x].l=min(tmp[x].l,tmp[y].l);//最短路 
 93     }
 94 }
 95  
 96 inline int query(int x,int y){//从x出发往上跳,找到海拔大于y的最小的点 
 97     for(int i=19;i>=0;i--){
 98         if(dep[x]-(1<<i)>0&&tmp[fa[x][i]].a>y)
 99             x=fa[x][i];
100     }
101     return tmp[x].l;//答案就是该点的权值 
102 }
103  
104 inline void solve(){
105     kruskal();
106     dfs(cnt,0);//从树根开始 
107     int q=read(),k=read(),s=read();
108     while(q--){
109         int x=(k*last+read()-1)%n+1,y=(k*last+read())%(s+1);
110         printf("%d\n",last=query(x,y));
111     }
112 }
113  
114 int main(){
115     T=read();
116     while(T--){
117         init();//初始化 
118         n=read(),m=read();
119         for(int i=1;i<=m;i++){
120             e[i].u=read(),e[i].v=read(),e[i].l=read(),e[i].a=read();
121             Add(e[i].u,e[i].v,e[i].l),Add(e[i].v,e[i].u,e[i].l);//建图 
122         }
123         dijkstra();
124         for(int i=1;i<=n;i++) tmp[i].l=dis[i];//重构树上叶子节点的权值为该点到1的最短路 
125         for(int i=n+1;i<=2*n;i++) tmp[i].l=INF;
126         memset(head,0,sizeof(head)),tot=0;
127         solve();
128     }
129 }

 

posted @ 2022-04-04 14:58  YHXo  阅读(36)  评论(0编辑  收藏  举报