zoukankan      html  css  js  c++  java
  • [HEOI2014][BZOJ3611] 大工程|虚树|树型dp|dfs序|树上倍增LCA

    3611: [Heoi2014]大工程

    Time Limit: 60 Sec  Memory Limit: 512 MB
    Submit: 288  Solved: 136
    [Submit][Status][Discuss]

    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 

    Source

     
    又是一道虚树的题目……然而难点变成了dp。
    前排orz PoPoQQQ大爷
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #define ll long long
    #define inf 2000000000
    #define N 1000005
    ll tot;
    int head[N],v[N],mx[N],mn[N],fa[N][20],deep[N],id[N],q[N],a[N],next[2*N],list[2*N],key[2*N];
    ll size[N],f[N];
    int n,k,top,cnt,dfn,ans1,ans2;
    using namespace std;
    inline ll read()
    {
        ll a=0,f=1; char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
        while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
        return a*f;
    }
    inline void insert(int x,int y)
    {
        if (x==y) return;
        next[++cnt]=head[x]; 
        head[x]=cnt;
        list[cnt]=y;
        key[cnt]=deep[y]-deep[x];
    }
    inline bool cmp(int a,int b)
    {
        return id[a]<id[b];
    }
    void dfs(int x)
    {
        id[x]=++dfn;
        for (int i=1;(1<<i)<=deep[x];i++) fa[x][i]=fa[fa[x][i-1]][i-1];
        for (int i=head[x];i;i=next[i])
            if (list[i]!=fa[x][0])
            {
                fa[list[i]][0]=x;
                deep[list[i]]=deep[x]+1;
                dfs(list[i]);
            }
    }
    inline int lca(int x,int y)
    {
        if (deep[x]<deep[y]) swap(x,y);
        int t=deep[x]-deep[y];
        for (int i=0;(1<<i)<=t;i++) 
            if ((1<<i)&t) x=fa[x][i];
        for (int i=19;i>=0;i--)
            if (fa[x][i]!=fa[y][i]) {x=fa[x][i]; y=fa[y][i];}
        return x==y?x:fa[x][0];
    }
    void dp(int x)
    {

        //size[x]表示以x为根的子树中有多少关键点  

        //f[x]表示以x为根的子树中所有关键点到x的距离之和  

        //mx[x]/mn[x]表示节点x为根的子树中的关键点到x的距离的最大/最小值  

        size[x]=v[x]; f[x]=0;
        mn[x]=v[x]?0:inf;
        mx[x]=v[x]?0:-inf;
        for (int i=head[x];i;i=next[i])
        {
            dp(list[i]);
            tot+=(f[x]+size[x]*key[i])*size[list[i]]+f[list[i]]*size[x];
            size[x]+=size[list[i]];
            f[x]+=f[list[i]]+key[i]*size[list[i]];
            ans1=min(ans1,mn[x]+mn[list[i]]+key[i]);
            ans2=max(ans2,mx[x]+mx[list[i]]+key[i]);
            mn[x]=min(mn[x],mn[list[i]]+key[i]);
            mx[x]=max(mx[x],mx[list[i]]+key[i]);
        }
        head[x]=0;
    }
    inline void solve()
    {
        top=cnt=0;
        int k=read();
        for (int i=1;i<=k;i++) {a[i]=read(); v[a[i]]=1;}
        sort(a+1,a+k+1,cmp);
        q[++top]=1;
        for (int i=1;i<=k;i++)
        {
            int t=a[i],f;
            while (top)
            {
                f=lca(q[top],t);
                if (top>1&&deep[q[top-1]]>deep[f]) {insert(q[top-1],q[top]); top--;}
                else if (deep[q[top]]>deep[f]) {insert(f,q[top]); top--; break;}
                else break;
            }
            if (q[top]!=f) q[++top]=f; q[++top]=t;
        }
        while (top>1) {insert(q[top-1],q[top]); top--;}
        ans1=inf; ans2=-inf; tot=0;
        dp(1);
        printf("%lld %d %d
    ",tot,ans1,ans2);
        for (int i=1;i<=k;i++) v[a[i]]=0;
    }
    int main()
    {
        n=read();
        for (int i=1;i<n;i++)
        {
            int u=read(),v=read();
            insert(u,v); insert(v,u);
        }
        dfs(1);
        int qn=read();
        memset(head,0,sizeof(head));
        while (qn--) solve();
        return 0;
    }
  • 相关阅读:
    【解题报告】NOIP2018
    【解题报告】NOIP2013
    【解题报告】NOIP2014
    【解题报告】NOIP2015
    【解题报告】NOIP2016
    【解题报告】CSPS2020
    【全程NOIP计划】初级数据结构1
    如何通过一个结构体成员变量的地址找到该结构体的首地址?[备忘]
    IGT笔试题,正整数N等于M个不同的正整数之和的问题
    Mac下Perl脚本如何运行
  • 原文地址:https://www.cnblogs.com/ws-fqk/p/4730129.html
Copyright © 2011-2022 走看看