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 }