zoukankan      html  css  js  c++  java
  • BZOJ 3611 [Heoi2014]大工程 虚树

    Description
    国家有一个大工程,要给一个非常大的交通网络里建一些新的通道。
    我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上。
    在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径。
    现在国家有很多个计划,每个计划都是这样,我们选中了 k 个点,然后在它们两两之间 新建 C(k,2)条 新通道。
    现在对于每个计划,我们想知道:
    1.这些新通道的代价和
    2.这些新通道中代价最小的是多少
    3.这些新通道中代价最大的是多少
    Input
    第一行 n 表示点数。

    接下来 n-1 行,每行两个数 a,b 表示 a 和 b 之间有一条边。
    点从 1 开始标号。 接下来一行 q 表示计划数。
    对每个计划有 2 行,第一行 k 表示这个计划选中了几个点。
    第二行用空格隔开的 k 个互不相同的数表示选了哪 k 个点。
    Output
    输出 q 行,每行三个数分别表示代价和,最小代价,最大代价。

    Sample Input
    10
    2 1
    3 2
    4 1
    5 2
    6 4
    7 5
    8 6
    9 7
    10 9
    5
    2
    5 4
    2
    10 4
    2
    5 2
    2
    6 1
    2
    6 1
    Sample Output
    3 3 3
    6 6 6
    1 1 1
    2 2 2
    2 2 2
    HINT
    n<=1000000

    q<=50000并且保证所有k之和<=2*n
    思路:
    20分:倍增+每次询问暴力 (但是为什么我的链剖暴力水过了60分… )
    50分:倍增+前缀和的暴力
    70分:点分治(可惜我并不会)
    100分:虚树

    主要讲讲100分的算法:
    首先 虚树是什么东西
    (你可以百度搜索“虚树”)
    先写一个倍增链剖神马的东西(求LCA快的就好)

    把读入按照原树的DFS序排序
    假设有三个点x,y,z。它们按照DFS序排序,那么lca(y,z)一定是lca(x,y)或者lca(y,z)。
    所以呢 我们可以这么做:
    维护一个栈 栈里面 是一条链
    1.如果栈为空 加进去此节点
    2.如果栈顶就是当前节点的祖先

    • 如果是一开始栈顶的祖先 直接把当前节点加进栈里
    • 否则把一开始栈顶的祖先加进栈里,再把当前节点加进栈里

    3.如果当前节点不是当前节点的祖先 弹出栈顶继续

    其实并不难 只是各种细节比较麻烦
    模拟一下 就全都出来了

    //By SiriusRen
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define N 2000050
    long long ans;
    int n,m,q,k[N],xx,yy,zz,maxx,minn;
    int v[N],next[N],first[N],tot=0,MAX[N],MIN[N],vis[N];
    int fa[N],wei[N],size[N],son[N],top[N],dfn[N],cnt;
    int s[N];
    void add(int x,int y){v[tot]=y,next[tot]=first[x],first[x]=tot++;}
    void dfs(int x,int weight){
        wei[x]=weight;size[x]=1,dfn[x]=++cnt;
        for(int i=first[x];~i;i=next[i]){
            if(v[i]!=fa[x]){
                fa[v[i]]=x;
                dfs(v[i],weight+1);
                size[x]+=size[v[i]];
                if(size[v[i]]>size[son[x]])son[x]=v[i];
            }
        }
    }
    void build(int x,int tp){
        top[x]=tp;
        if(son[x])build(son[x],tp);
        for(int i=first[x];~i;i=next[i])
            if(v[i]!=fa[x]&&v[i]!=son[x])
                build(v[i],v[i]);
    }
    int lca(int x,int y){
        int fx=top[x],fy=top[y];
        while(fx!=fy){
            if(wei[fx]<wei[fy])
                swap(x,y),swap(fx,fy);
            x=fa[fx],fx=top[x];
        }
        if(x>y)return y;
        return x;
    }
    void dfs2(int x){
        MAX[x]=0,MIN[x]=0x3fffffff,size[x]=vis[x];
        for(int i=first[x];~i;i=next[i]){
            if(v[i]==x)continue;
            dfs2(v[i]);
            size[x]+=size[v[i]];
            int dis=wei[v[i]]-wei[x];
            ans+=1LL*size[v[i]]*(m-size[v[i]])*dis;
            maxx=max(maxx,MAX[x]+MAX[v[i]]+dis);
            minn=min(minn,MIN[x]+MIN[v[i]]+dis);
            MAX[x]=max(MAX[x],MAX[v[i]]+dis);
            MIN[x]=min(MIN[x],MIN[v[i]]+dis);
        }
        if(vis[x]){
            maxx=max(maxx,MAX[x]);
            minn=min(minn,MIN[x]);
            MIN[x]=0;
        }
        first[x]=-1;
    }
    bool cmp(int x,int y){return dfn[x]<dfn[y];}
    int main(){
        memset(first,-1,sizeof(first));
        scanf("%d",&n);
        for(int i=1;i<n;i++){
            scanf("%d%d",&xx,&yy);
            add(xx,yy),add(yy,xx);
        }
        fa[1]=1;dfs(1,0),build(1,1);
        memset(first,-1,sizeof(first));
        memset(size,0,sizeof(size));
        scanf("%d",&q);
        while(q--){
            int tp=0;tot=ans=maxx=0,minn=0x3fffffff;
            scanf("%d",&m);
            for(int i=1;i<=m;i++)scanf("%d",&k[i]),vis[k[i]]++;
            sort(k+1,k+1+m,cmp); 
            for(int i=1;i<=m;i++){
                if(!tp){s[++tp]=k[i];continue;}
                int LCA=lca(k[i],s[tp]);
                while(1){
                    if(dfn[LCA]>=dfn[s[tp-1]]){
                        add(LCA,s[tp]);
                        if(s[--tp]!=LCA)s[++tp]=LCA;
                        break;
                    }
                    add(s[tp-1],s[tp]),tp--;
                }
                s[++tp]=k[i];
            }
            while(tp>1)add(s[tp-1],s[tp]),tp--;
            dfs2(s[1]);
            for(int i=1;i<=m;i++)vis[k[i]]=0;
            printf("%lld %d %d
    ",ans,minn,maxx);
        }
    }

    这里写图片描述

    这里再放一发 题解的 写得挺好的
    嗯嗯

    #include<cstdio> 
    #include<iostream> 
    #include<cmath> 
    #include<cstring> 
    #include<algorithm> 
    #define maxn 1000005 
    #define inf 1061109567 
    using namespace std; 
    typedef long long int64; 
    char ch; 
    bool ok; 
    void read(int &x){ 
        for (ok=0,ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') ok=1; 
        for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar()); 
        if (ok) x=-x; 
    } 
    int n,q,a,b,k; 
    int idx,fa[maxn][21],dfn[maxn],dis[maxn],cnt,top,list[maxn],stack[maxn],bo[maxn]; 
    int ans1,ans2,siz[maxn],g[2][maxn]; 
    int64 f[maxn]; 
    bool cmp(int a,int b){return dfn[a]<dfn[b];} 
    struct Graph{ 
        int tot,now[maxn],son[maxn<<1],pre[maxn<<1]; 
        void init(){tot=0,memset(now,0,sizeof(now));} 
        inline void put(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;} 
        inline void dfs1(int u){ 
            dfn[u]=++idx; 
            for (int i=0;fa[u][i];i++) fa[u][i+1]=fa[fa[u][i]][i]; 
            for (int p=now[u],v=son[p];p;p=pre[p],v=son[p]) 
                if (v!=fa[u][0]) fa[v][0]=u,dis[v]=dis[u]+1,dfs1(v); 
        } 
        inline void dfs2(int u){ 
            siz[u]=bo[u]; 
            f[u]=0,g[0][u]=inf,g[1][u]=0; 
            for (int p=now[u],v=son[p];p;p=pre[p],v=son[p]){ 
                dfs2(v),siz[u]+=siz[v]; 
                int d=dis[v]-dis[u]; 
                f[u]+=f[v]+1LL*siz[v]*(k-siz[v])*d; 
                ans1=min(ans1,g[0][u]+g[0][v]+d),ans2=max(ans2,g[1][u]+g[1][v]+d); 
                g[0][u]=min(g[0][u],g[0][v]+d); 
                g[1][u]=max(g[1][u],g[1][v]+d); 
            } 
            if (bo[u]) ans1=min(ans1,g[0][u]),ans2=max(ans2,g[1][u]),g[0][u]=0; 
            now[u]=0; 
        } 
    }G1,G2; 
    void swim(int &u,int h){for (int i=20;h;i--) if (h>=(1<<i)) h-=(1<<i),u=fa[u][i];} 
    int calc_lca(int u,int v){ 
        if (dis[u]<dis[v]) swap(u,v); 
        swim(u,dis[u]-dis[v]); 
        if (u==v) return u; 
        for (int i=20;i>=0;i--) if (fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i]; 
        return fa[u][0]; 
    } 
    int main(){ 
        read(n); 
        for (int i=1;i<n;i++) read(a),read(b),G1.put(a,b),G1.put(b,a); 
        G1.dfs1(1); 
        for (read(q);q;q--){ 
            read(k),cnt=top=0; 
            for (int i=1;i<=k;i++) read(list[i]),bo[list[i]]=1; 
            sort(list+1,list+k+1,cmp); 
            for (int i=1;i<=k;i++){ 
                if (!top){stack[++top]=list[i];continue;} 
                int lca=calc_lca(stack[top],list[i]); 
                while (dfn[lca]<dfn[stack[top]]){ 
                    if (dfn[lca]>=dfn[stack[top-1]]){ 
                        G2.put(lca,stack[top]); 
                        if (stack[--top]!=lca) stack[++top]=lca; 
                        break; 
                    } 
                    G2.put(stack[top-1],stack[top]),top--; 
                } 
                stack[++top]=list[i]; 
            } 
            while (top>1) G2.put(stack[top-1],stack[top]),top--; 
            ans1=inf,ans2=0; 
            G2.dfs2(stack[1]); 
            printf("%lld %d %d
    ",f[stack[1]],ans1,ans2); 
            for (int i=1;i<=k;i++) bo[list[i]]=0; 
            G2.tot=0; 
        } 
        return 0; 
    }
  • 相关阅读:
    PostgreSQL杂记页
    程序员给女朋友周年纪念的浪漫
    实现乐鑫esp8266的无线OTA升级,实现远程在线升级固件
    XAOP的使用示范例子
    XPage的使用示范例子
    Python手势识别与控制
    Python人体肤色检测
    基于Opencv自带BP网络的车标简易识别
    iOS 自定义转场动画
    Python实时语音识别控制
  • 原文地址:https://www.cnblogs.com/SiriusRen/p/6532270.html
Copyright © 2011-2022 走看看