P4768 [NOI2018] 归程
题目描述
本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定。
魔力之都可以抽象成一个 \(n\) 个节点、\(m\) 条边的无向连通图(节点的编号从 \(1\) 至 \(n\))。我们依次用 \(l,a\) 描述一条边的长度、海拔。
作为季风气候的代表城市,魔力之都时常有雨水相伴,因此道路积水总是不可避免的。由于整个城市的排水系统连通,因此有积水的边一定是海拔相对最低的一些边。我们用水位线来描述降雨的程度,它的意义是:所有海拔不超过水位线的边都是有积水的。
Yazid 是一名来自魔力之都的 OIer,刚参加完 ION2018 的他将踏上归程,回到他温暖的家。Yazid 的家恰好在魔力之都的 \(1\) 号节点。对于接下来 \(Q\) 天,每一天 Yazid 都会告诉你他的出发点 \(v\) ,以及当天的水位线 \(p\)。
每一天,Yazid 在出发点都拥有一辆车。这辆车由于一些故障不能经过有积水的边。Yazid 可以在任意节点下车,这样接下来他就可以步行经过有积水的边。但车会被留在他下车的节点并不会再被使用。
需要特殊说明的是,第二天车会被重置,这意味着:
- 车会在新的出发点被准备好。
- Yazid 不能利用之前在某处停放的车。
Yazid 非常讨厌在雨天步行,因此他希望在完成回家这一目标的同时,最小化他步行经过的边的总长度。请你帮助 Yazid 进行计算。
数据范围与约定
所有测试点均保证 \(T\leq 3\),所有测试点中的所有数据均满足如下限制:
-
\(n\leq 2\times 10^5\),\(m\leq 4\times 10^5\),\(Q\leq 4\times 10^5\),\(K\in\left\{0,1\right\}\),\(1\leq S\leq 10^9\)。
-
对于所有边:\(l\leq 10^4\),\(a\leq 10^9\)。
-
任意两点之间都直接或间接通过边相连。
-
强制在线
Solution:
kruskal 重构树魅力时刻。
其实如果写过 P4197 Peaks 的话那么这题几乎是纯送的。
先分析题意:
我们先求出原图上以1为起点的单源最短路,然后题目要求我们在一个联通图上求出从一个点 \(u\) 出发只经过满足 $ h > p$ 的边能到达的所有节点 \(v\) 中 \(dis_{v}\) 的最小值。
实现:
我们发现 $ h > p$ 这个条件能通过kruskal重构树来构造.我们建一颗以 \(h\) 构成小根堆的kruskal重构树,叶子节点存的是 \(dis\) 然后对于每次查询,我们从节点 \(u\) 一直倍增向上跳到满足$ h > p$且深度最小的节点 \(u'\) 然后答案就是节点 \(u'\) 子树下的 \(dis\) 的最小值。
至于如何查询 \(u'\) 的子树,这里我选的是在欧拉序上查 st表。
Code:
#include<bits/stdc++.h>
const int N=4e5+5;
const int inf=1e9;
const int lg=20;
using namespace std;
int n,m,days,ans;
vector<tuple<int,int> > E[N];
vector<int> e[N<<1];
int val[N],dis[N];
struct Edge{int x,y,w;bool operator <(const Edge &e)const{return w>e.w;}
}q[N];
struct Kruskal{int fa[N],tot;int find(int x){return fa[x]= fa[x]==x ? fa[x] : find(fa[x]);}void add(int x,int y){e[x].emplace_back(y);}void build(){tot=n;for(int u=1;u<=n;u++)fa[u]=u;for(int i=1;i<=m;i++){int x=q[i].x,y=q[i].y,w=q[i].w;int u=find(x),v=find(y);if(u!=v){val[++tot]=w;add(tot,v);add(tot,u);//cout<<"add:"<<tot<<" : "<<u<<" "<<v<<"="<<w<<"\n";fa[tot]=fa[u]=fa[v]=tot;}}}}K;
struct Dijkstra{struct Node{int x,val;bool operator<(const Node &n)const{return n.val<val;}};priority_queue<Node> Q;int vis[N];void init(){for(int i=1;i<=n;i++)dis[i]=inf,vis[i]=0;}void dijksta(int s){init();dis[s]=0;Q.push({s,dis[s]});while(!Q.empty()){int x=Q.top().x;Q.pop();if(vis[x])continue;vis[x]=1;for(auto [y,w] : E[x]){if(dis[y]>dis[x]+w){dis[y]=dis[x]+w;Q.push({y,dis[y]});}}}}
}D;
struct Graph{int dfn[N<<1];int f[N<<1][lg+5];int st[N<<1],ed[N<<1];void dfs(int x,int fa){st[x]=++dfn[0];f[x][0]=fa;for(int j=1;j<=lg;j++)f[x][j]=f[f[x][j-1]][j-1];for(auto y : e[x]){if(y==fa)continue;dfs(y,x);}ed[x]=dfn[0];}int find(int x,int k){for(int j=lg;j>=0;j--)if(val[f[x][j]]>k)x=f[x][j];return x;}
}G;
struct ST{int LG[N<<1];int st[N<<1][lg+5];int R;void init(){R=(n<<1)-1;for(int i=2;i<=R;i++)LG[i]=LG[i>>1]+1;for(int i=1;i<=R;i++)st[i][0]=inf;for(int i=1;i<=n;i++)st[G.st[i]][0]=dis[i];}void build(){init();//cout<<"R:"<<R<<"\n";//for(int i=1;i<=R;i++)cout<<st[i][0]<<(i==R ? "\n" : " ");for(int j=1;j<=lg;j++)for(int i=1;i<=R;i++){int tmp=min(i+(1<<j-1),R);st[i][j]=min(st[i][j-1],st[tmp][j-1]);}}int query(int l,int r){int k=LG[r-l+1];return min(st[l][k],st[r-(1<<k)+1][k]);}
}S;
struct Change{int k,s;int chage_v(int x){return (x+k*ans-1)%n+1;}int chage_p(int x){return (x+k*ans)%(s+1);}
}C;
void Clear()
{int R=(n<<1)-1;G.dfn[0]=0;for(int i=1;i<=n;i++)E[i].clear();for(int i=1;i<=R;i++)e[i].clear(),val[i];
}
void work()
{cin>>n>>m;ans=0;for(int i=1,x,y,w,h;i<=m;i++){scanf("%d%d%d%d",&x,&y,&w,&h);E[x].emplace_back(y,w);E[y].emplace_back(x,w);q[i]={x,y,h};}sort(q+1,q+1+m);D.dijksta(1);K.build();G.dfs(K.tot,0);S.build();cin>>days>>C.k>>C.s;for(int i=1,v,p;i<=days;i++){scanf("%d%d",&v,&p);v=C.chage_v(v);p=C.chage_p(p);//cout<<"v p : "<<v<<" "<<p<<"\n";v=G.find(v,p);ans=S.query(G.st[v],G.ed[v]);//cout<<v<<"="<<G.st[v]<<" "<<G.ed[v]<<"\n";printf("%d\n",ans);}//cout<<"END\n";Clear();
}
int main()
{//freopen("P4768_2.in","r",stdin);freopen("P4768.out","w",stdout);int T;cin>>T;while(T--)work();return 0;
}