zoukankan      html  css  js  c++  java
  • P4768 [NOI2018]归程(Kruscal重构树)

    这个算法是因为借用了kruscal合并并查集集合维护连通性,并且重建树不破坏结构得名。

    具体的思想是:

    我们按照边权排序(本题从大到小排序)

    我们在不断合并集合的同时,每次合并两个集合的祖先,都建立一个虚拟原点,使得这个点指向两个祖先,并且边权等于点权。这就是kruscal重构树,本质上是一个二叉堆。

    这样做的好处是,树上的点权都是符合单调性的,可以解决一类只能经过大于指定长度的边的图论问题。我们只需要倍增跳跃,满足条件的子树都是互通的。

    对于这一题,先用最短路维护答案,建树后,dfs就能求得满足边不小于指定长度的集合的到终点的最小距离。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<ll,ll> pll;
    const int N=1e6+10;
    const int M=2e6+10;
    const int inf=0x3f3f3f3f;
    int n,m,Q,K,S;
    int f[N][22],h[N],ne[N],e[N],idx;
    int h1[N],ne1[N],e1[N],num;
    ll last,d[N];
    int p[N],depth[N],w[N],st[N];
    struct node{
        ll a,b,c,d;//c:长度 d:海拔
    }tr[N],ans[N];
    void add1(int a,int b,int c){
        e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
    }
    void add(int a,int b){
        e1[num]=b,ne1[num]=h1[a],h1[a]=num++;
    }
    void dij(){
        memset(d,0x3f,sizeof d);
        memset(st,0,sizeof st);
        d[1]=0;
        priority_queue<pll,vector<pll>,greater<pll>> q;
        q.push({d[1],1});
        while(q.size()){
            auto t=q.top();
            q.pop();
            if(st[t.second])
                continue;
            st[t.second]=1;
            for(int i=h[t.second];i!=-1;i=ne[i]){
                int j=e[i];
                if(d[j]>d[t.second]+w[i]){
                    d[j]=d[t.second]+w[i];
                    q.push({d[j],j});
                }
            }
        }
    
        for(int i=1;i<=n;i++)
             ans[i].c=d[i];
    }
    void init(){
        num=idx=last=0;
        memset(f,0,sizeof f);
        memset(h1,-1,sizeof h1);
        memset(h,-1,sizeof h);
    }
    int find(int x){
        if(p[x]!=x){
            p[x]=find(p[x]);
        }
        return p[x];
    }
    bool cmp(node a,node b){
        return a.d>b.d;
    }
    void dfs(int u,int pa){//求取krusacl重构树上的最小值,因为子树上的点可以互相到达
        depth[u]=depth[pa]+1;
        f[u][0]=pa;
        int i;
        for(i=1;i<=20;i++) f[u][i]=f[f[u][i-1]][i-1];
        for(i=h1[u];i!=-1;i=ne1[i]){
            int j=e1[i];
            dfs(j,u);
            ans[u].c=min(ans[u].c,ans[j].c);
        }
    }
    ll query(int x,int y){//倍增跳到最上面满足条件的点
        for(int i=20;i>=0;--i) if(depth[x]-(1<<i)>0&&ans[f[x][i]].d>y) x=f[x][i];
        return ans[x].c;
    }
    void solve(){
        int i;
        for(i=1;i<=2*n;i++)
            p[i]=i;
        sort(tr+1,tr+1+m,cmp);
        int tot=0,cnt=n;
        for(i=1;i<=m;i++){//建立虚拟点,并用点权代替边权
            int pa=find(tr[i].a);
            int pb=find(tr[i].b);
            if(pa!=pb){
                add(++cnt,pa);//因为并查集破坏结构因此重建树
                add(cnt,pb);
                p[pa]=cnt;
                p[pb]=cnt;
                ans[cnt].d=tr[i].d;
                tot++;
            }
            if(tot==n-1)
                break;
        }
        dfs(cnt,0);
        while(Q--){
            int u,v;
            cin>>u>>v;
            int x=(K*last+u-1)%n+1,y=(K*last+v)%(S+1);
            cout<<(last=query(x,y))<<endl;
        }
    }
    int main(){
        ios::sync_with_stdio(false);
        int t;
        cin>>t;
        while(t--){
            cin>>n>>m;
            init();
            int i;
            for(i=1;i<=m;i++){
                int a,b,c,d;
                cin>>a>>b>>c>>d;
                tr[i]={a,b,c,d};//kruscal存边
                add1(a,b,c);
                add1(b,a,c);
            }
            for(i=n+1;i<=n*2;i++)
                ans[i].c=0x3f3f3f3f;//初始点不存在的点先赋值为极大值
            dij();
            cin>>Q>>K>>S;
            solve();
        }
    }
    View Code
    没有人不辛苦,只有人不喊疼
  • 相关阅读:
    PAT(A) 1065. A+B and C (64bit) (20)
    PAT(A) 1046. Shortest Distance (20)
    PAT(A) 1042. Shuffling Machine (20)
    PAT(A) 1009. Product of Polynomials (25)
    PAT(A) 1002. A+B for Polynomials (25)
    win10开始菜单打不开怎么办?
    排序
    C语言文件读写操作总结
    NO.3 4 章 模拟、算法初步
    POJ2104 K-th Number(归并树)
  • 原文地址:https://www.cnblogs.com/ctyakwf/p/13632566.html
Copyright © 2011-2022 走看看