BZOJ5415 [Noi2018]归程(洛谷P4768)

Kruskal重构树

BZOJ题目传送门
洛谷题目传送门

这道题做法很多,我所知道的还有可持久化并查集(同学写了这个),因为离线是用并查集做,那么在线就可持久化一下。BZOJ上有可持久化并查集这道题。

这里采用Kruskal重构树。先Dijkstra+heap预处理出其他点到1的最短路,再以海拔为第一关键字构造Kruskal重构树(最大生成树)。这时是一个小根堆。可以发现当一个节点没有被淹没时,其子树也一定不会被淹没。一遍DFS求出以 i 为根的子树中到1的最小距离。每次询问跳LCA即可。注意带上海拔的条件。

代码:

#include<queue>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 600005
#define F inline
using namespace std;
struct edge{ int nxt,to,d; }ed[N<<1];
struct edg{ int x,y,z; }e[N];
struct P{ int x,d; };
int T,n,m,k,ans,nd,h[N],ff[N],dep[N],t[N][2],fa[N][21],d[N],w[N];
priority_queue <P> q; bool f[N];
F char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    return l==r?EOF:*l++;
}
F int _read(){
    int x=0; char ch=readc();
    while (!isdigit(ch)) ch=readc();
    while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
    return x;
}
F void writec(int x){ if (x>9) writec(x/10); putchar(x%10+48); }
F void _write(int x){ writec(x),puts(""); }
F bool operator <(P a,P b){ return a.d>b.d; }
F bool cmp(edg a,edg b){ return a.z>b.z; }
#define add(x,y,z) ed[++k]=(edge){h[x],y,z},h[x]=k
F void Dij(){
    while (!q.empty()) q.pop();
    for (int i=2;i<n<<1;i++) d[i]=1e9,f[i]=false;
    q.push((P){1,0}),f[1]=false;
    while (!q.empty()){
        int x=q.top().x; q.pop();
        if (f[x]) continue; f[x]=true;
        for (int i=h[x],v;i;i=ed[i].nxt)
            if (!f[v=ed[i].to]&&d[x]+ed[i].d<d[v])
                d[v]=d[x]+ed[i].d,q.push((P){v,d[v]});
    }
}
int findfa(int x){ return x==ff[x]?x:ff[x]=findfa(ff[x]); }
void dfs(int x){
    if (!x) return; dep[x]=dep[fa[x][0]]+1;
    dfs(t[x][0]),dfs(t[x][1]);
    if (x>n) d[x]=min(d[x],min(d[t[x][0]],d[t[x][1]]));
}
F void Make(){
    for (int j=1;j<21;j++)
        for (int i=1;i<n<<1;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
}
F int LCA(int x,int p){
    for (int j=20;~j;j--)
        if (dep[fa[x][j]]&&w[fa[x][j]]>p) x=fa[x][j];
    return d[x];
}
int main(){
    for (T=_read();T;T--){
        nd=n=_read(),m=_read(),k=ans=0;
        for (int i=1;i<=n;i++) h[i]=0;
        for (int i=1,x,y,l,a;i<=m;i++){
            x=_read(),y=_read(),l=_read(),a=_read();
            add(x,y,l),add(y,x,l),e[i]=(edg){x,y,a};
        }
        sort(e+1,e+m+1,cmp),Dij();
        for (int i=1;i<n<<1;i++)
            ff[i]=i,w[i]=0,t[i][0]=t[i][1]=0;
        for (int i=1,s=0,x,y,fx,fy;s<n-1;i++)
            if ((fx=findfa(x=e[i].x))!=(fy=findfa(y=e[i].y))){
                t[++nd][0]=fx,t[nd][1]=fy,w[nd]=e[i].z;
                ff[fx]=ff[fy]=fa[fx][0]=fa[fy][0]=nd,s++;
            }
        dfs(nd),Make();
        for (int q=_read(),k=_read(),s=_read();q;q--){
            int v=(_read()+k*ans-1)%n+1,p=(_read()+k*ans)%(s+1);
            _write(ans=LCA(v,p));
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/a1799342217/article/details/81368592