NOI2018 Day1 归程(Kruskal重构树)

NOI2018 Day1 return

题解

作为NOI Day1 的T1,这道题目还是比较清真的(虽然自己在同步赛的时候只打了70分的部分分,然后数组开小了挂成了55分。。)正解的方法似乎有很多,可持久化并查集什么的都可以做。但个人认为还是Kruskal重构树的方法比较好些,也比较容易一点。 Kruskal重构树的基础还是像Kruskal生成树一样,先把边sort,再一条一条加进生成树中。而重构树唯一不同的点是当重构树在进行两个联通块合并的时候,并不是直接将其合并,而是新建一个父节点,其点权为当前枚举到的边的边权。这样显然的Kruskal重构树就会有两个比较重要的性质了:
①这颗重构树是个二叉树(虽然在这里并没有什么用)
②这棵树是个大(小)根堆
知道了这一点我们就可以开始做这道题了。我们先按海拔从大到小将每条边的排序,然后做Kruskal重构树,这样我们生成的Kruskal重构树就是一棵以海拔为关键字的小根堆,同时他会有一些性质:如果一条边被水淹没了(在重构树中就是那条边所对应的点),那么这个节点的所有父节点都会被水淹没,并且如果这条边没有被水淹没,那么它的子树的任意一个点也不会被水淹没,于是这棵子树的任意两个点都可以通过开车到达。于是我们就可以先跑一边最短路(出题人:SPFA死掉了),然后在进行构造Kruskal重构树的时候维护子树中到根的最短距离Mn。对于每一个询问,我们只需要倍增在重构树上找到未被水淹没的深度最浅的节点,然后返回这个节点的Mn的值就行了,还是比较好写的。

AC代码:

#pragma GCC optimize (2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('
');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
const int maxn=4e5+500;
typedef pair<ll,int>P;
#define fi first
#define se second
int n,m,T,tot,waytot,ndcnt,Q,k,s,cnt;
ll ans;
int head[maxn],fa[maxn<<1],val[maxn<<1],Head[maxn<<1],deep[maxn<<1],p[maxn][22];
ll dis[maxn],Mn[maxn<<1];
bool vis[maxn];
struct edge {
    int to,nxt,l,h;
}E[maxn<<2];
struct sortedge {
	int f,t,l,h;
	bool operator < (const sortedge &rhs) const {
		return h>rhs.h;
	}
}EE[maxn<<2];
struct way {
    int to,nxt;
}G[maxn<<4];
/*==================Define Area================*/
void FO() {
    freopen("return.in","r",stdin);
    freopen("return.out","w",stdout);
}

namespace doit {
    void init() {
        ans=tot=waytot=cnt=0;
        memset(head,-1,sizeof head);
        memset(Head,-1,sizeof Head);
        for(int i=1;i<=200000;i++) {
            fa[i]=i;
        }
    }

    void addedge(int u,int v,int h,int l) {
        E[++tot].to=v;E[tot].nxt=head[u];head[u]=tot;E[tot].h=h;E[tot].l=l;
        E[++tot].to=u;E[tot].nxt=head[v];head[v]=tot;E[tot].h=h;E[tot].l=l;
    }

    void Dj() {
        priority_queue<P,vector<P>,greater<P> >Q;
        while(!Q.empty()) Q.pop();
        memset(vis,0,sizeof vis);
        memset(dis,0x7f,sizeof dis);
        dis[1]=0;
        Q.push(P(dis[1],1));
        while(!Q.empty()) {
            P p=Q.top();Q.pop();
            int idx=p.se;
            ll dist=p.fi;
            vis[idx]=1;
            for(int i=head[idx];~i;i=E[i].nxt) {
                int to=E[i].to;
                if(vis[to]) continue;
                if(dis[to]>dist+E[i].l) {
                    dis[to]=dist+E[i].l;
                    Q.push(P(dis[to],to));
                }
            }
        }
    }
}

namespace KruskalTree {
    void addway(int u,int v) {
        G[++waytot].to=v;G[waytot].nxt=Head[u];Head[u]=waytot;
        G[++waytot].to=u;G[waytot].nxt=Head[v];Head[v]=waytot;
    }

    int find(int x) {
        return fa[x]==x?x:fa[x]=find(fa[x]);
    }

    void unite(int x,int y,int v) {
        int fx=find(x),fy=find(y);
        if(fx==fy) return ;
        ++ndcnt;
        addway(fx,ndcnt);
        addway(fy,ndcnt);
        Mn[ndcnt]=min(Mn[fx],Mn[fy]);
        fa[ndcnt]=ndcnt;
        fa[fx]=ndcnt;
        fa[fy]=ndcnt;
        val[ndcnt]=v;
        return ;
    }

    void Kruskal() {
        ndcnt=n;
        for(int i=1;i<=n;i++) {
            Mn[i]=dis[i];
        }
        for(int i=1;i<=cnt;i++) {
            unite(EE[i].f,EE[i].t,EE[i].h);
        }
    }
}

namespace LCA {
    void dfs(int o,int ff,int dep) {
        deep[o]=dep;
        p[o][0]=ff;
        for(int i=Head[o];~i;i=G[i].nxt) {
            int to=G[i].to;
            if(to==ff) continue;
            dfs(to,o,dep+1);
        }
    }

    void Cal() {
        for(int i=1;i<=20;i++) {
            for(int j=1;j<=ndcnt;j++) {
                p[j][i]=p[p[j][i-1]][i-1];
            }
        }
    }

    int Solve(ll o,ll a) {
        for(int i=20;i>=0;i--) {
            if(p[o][i]&&val[p[o][i]]>a) o=p[o][i]; 
        }
        return o;
    }
}
using namespace doit;
using namespace KruskalTree;
using namespace LCA;

int main() {
    FO();
    read(T);
    while(T--) {
        init();
        read(n);read(m);
        for(int i=1,u,v,h,l;i<=m;i++) {
            read(u);read(v);read(l);read(h);
            addedge(u,v,h,l);
            EE[++cnt].f=u;EE[cnt].t=v;EE[cnt].l=l;EE[cnt].h=h;
        }
        Dj();
        sort(EE+1,EE+1+cnt);
        Kruskal();
        dfs(ndcnt,0,1);
        Cal();
        read(Q);read(k);read(s);
        while(Q--) {
            ll v,p;
            read(v);read(p);
            v=(v+k*ans-1)%n+1;
            p=(p+k*ans)%(s+1);
            int o=Solve(v,p);
            ans=Mn[o];
            printf("%lld
",ans);
        }
    }
}
「我不敢下苦功琢磨自己,怕终于知道自己并非珠玉;然而心中既存着一丝希冀,便又不肯甘心与瓦砾为伍。」
原文地址:https://www.cnblogs.com/Apocrypha/p/9430500.html