zoukankan      html  css  js  c++  java
  • P2495 [SDOI2011]消耗战 (虚树)

    虚树对于多次询问的时候有这优化复杂度的好处,其原理就是只保留有用节点和必须保留的有用节点的lca

    先求一遍dfs序后,用栈模拟操作,根据lca是否在栈中确定是否要将lca加入栈,因为重构了树,因此原来的边的信息需要进行更换

    对于本题,新的边其实就是路径上的边权的最小值,这是因为随便切掉一个边就能把他们断开。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=5e5+10;
    const int mod=998244353;
    struct node{
        int x;
        ll w;
    };
    vector<node> g[N],g1[N];
    int n;
    int dfn[N],times;
    int f[N][25],depth[N];
    ll cost[N][25];
    int st[N];
    int cnt[N];
    int q[N];
    ll dp[N];
    void dfs(int u,int fa,ll w){
        dfn[u]=++times;
        depth[u]=depth[fa]+1;
        f[u][0]=fa;cost[u][0]=w;
        int i;
        for(i=1;i<=21;i++){
            f[u][i]=f[f[u][i-1]][i-1];
            cost[u][i]=min(cost[f[u][i-1]][i-1],cost[u][i-1]);
        }
        for(i=0;i<g1[u].size();i++){
            int v=g1[u][i].x;
            if(v==fa)
                continue;
            dfs(v,u,g1[u][i].w);
        }
    }
    bool cmp(int x,int y){
        return dfn[x]<dfn[y];
    }
    int lca(int a,int b){
        if(depth[a]<depth[b])
            swap(a,b);
        int i;
        for(i=21;i>=0;i--){
            if(depth[f[a][i]]>=depth[b]){
                a=f[a][i];
            }
        }
        if(a==b)
            return a;
        for(i=21;i>=0;i--){
            if(f[a][i]!=f[b][i]){
                a=f[a][i];
                b=f[b][i];
            }
        }
        return f[a][0];
    }
    ll query(int a,int b){
        ll ans=1e18;
        if(depth[a]>depth[b])
            swap(a,b);
        for(int i=21;i>=0;i--){
            if(depth[f[b][i]]>=depth[a]){
                ans=min(ans,cost[b][i]);
                b=f[b][i];
            }
        }
        return ans;
    }
    void add(int a,int b){
        ll num=query(a,b);
        g[a].push_back({b,num});
    }
    void get(int u){
        int i;
        for(i=0;i<g[u].size();i++){
            int v=g[u][i].x;
            get(v);
            if(st[v])
                dp[u]+=g[u][i].w;
            else
                dp[u]+=min(g[u][i].w,dp[v]);
            st[v]=0;dp[v]=0;
        }
        g[u].clear();
    }
    int main(){
        ios::sync_with_stdio(false);
        int i;
        cin>>n;
        for(i=1;i<n;i++){
            int a,b,c;
            cin>>a>>b>>c;
            g1[a].push_back({b,c});
            g1[b].push_back({a,c});
        }
        //cout<<"aa"<<endl;
        dfs(1,0,0);
        int m;
        cin>>m;
        while(m--){
            int k;
            cin>>k;
            for(i=1;i<=k;i++){
                int x;
                cin>>x;
                st[x]=1;
                cnt[i]=x;
            }
            sort(cnt+1,cnt+1+k,cmp);
            q[1]=1;
            int tt=1;
            for(i=1;i<=k;i++){
                if(cnt[i]==1)
                    continue;
                int p=lca(cnt[i],q[tt]);
                if(p!=q[tt]){
                    while(dfn[p]<dfn[q[tt-1]]){
                        add(q[tt-1],q[tt]);
                        tt--;
                    }
                    if(dfn[p]!=dfn[q[tt-1]]){
                        add(p,q[tt]);
                        q[tt]=p;
                    }
                    else{
                        add(p,q[tt]);
                        tt--;
                    }
                }
                q[++tt]=cnt[i];
            }
            for(i=1;i<tt;i++){
                add(q[i],q[i+1]);
            }
            get(1);
            cout<<dp[1]<<endl;
            dp[1]=0;
        }
        return 0;
    }
    View Code
    没有人不辛苦,只有人不喊疼
  • 相关阅读:
    多重继承
    单继承
    访问限制方法
    猜字游戏
    getSet方法
    访问限制
    __str__函数
    析构函数
    构造函数二
    选择排序
  • 原文地址:https://www.cnblogs.com/ctyakwf/p/13360623.html
Copyright © 2011-2022 走看看