• bzoj3611 [Heoi2014]大工程


    3611: [Heoi2014]大工程

    Time Limit: 60 Sec  Memory Limit: 512 MB
    Submit: 1945  Solved: 811
    [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 
    分析:个人感觉比较简单的一道题.
       选中k个点,很显然,要用虚树. 求总和,最小值,最大值肯定都是用dp来做.
       求总和的话计算每条边的贡献,也就是这条边一端有多少个特殊点,另一端有多少个特殊点,乘起来再乘上这条边的权值就是这条边的贡献了.
       求最小值/最大值则利用的是前缀和的思想.一条经过根节点的路径,它的两端肯定是在两棵不同的子树内的.那么可以把这条路径分成两半.记录前面的子树中一半路径的最值,再拼上当前子树中的路径的长度,取个最值就能用来更新答案了.
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    const ll maxn = 1000010,inf = 1e17;
    ll n,head[maxn],to[maxn * 2],nextt[maxn * 2],tot = 1,deep[maxn],fa[maxn][22];
    ll pos[maxn],dfs_clock,Q,sta[maxn],top,a[maxn],b[maxn],k,sizee[maxn],flag[maxn],Time;
    ll maxx[maxn],minn[maxn],ans1,ans2,ans;
    
    void add(ll x,ll y)
    {
        to[tot] = y;
        nextt[tot] = head[x];
        head[x] = tot++;
    }
    
    void add2(ll x,ll y)
    {
        if (x == y)
            return;
        to[tot] = y;
        nextt[tot] = head[x];
        head[x] = tot++;
    }
    
    ll lca(ll x,ll y)
    {
        if (deep[x] < deep[y])
            swap(x,y);
        for (ll i = 21; i >= 0; i--)
            if (deep[fa[x][i]] >= deep[y])
                x = fa[x][i];
        if (x == y)
            return x;
        for (int i = 21; i >= 0; i--)
            if (fa[x][i] != fa[y][i])
            {
                x = fa[x][i];
                y = fa[y][i];
            }
        return fa[x][0];
    }
    
    void dfs(ll u,ll faa)
    {
        fa[u][0] = faa;
        deep[u] = deep[faa] + 1;
        pos[u] = ++dfs_clock;
        for (ll i = head[u]; i; i = nextt[i])
        {
            ll v = to[i];
            if (v == faa)
                continue;
            dfs(v,u);
        }
    }
    
    bool cmp(ll x,ll y)
    {
        return pos[x] < pos[y];
    }
    
    void dp(ll u)
    {
        if (flag[u] == Time)
        {
            sizee[u] = 1;
            maxx[u] = minn[u] = 0;
        }
        else
        {
            sizee[u] = 0;
            maxx[u] = -inf;
            minn[u] = inf;
        }
        for (ll i = head[u]; i; i = nextt[i])
        {
            ll v = to[i],w = deep[v] - deep[u];
            dp(v);
            sizee[u] += sizee[v];
            ans1 = min(ans1,minn[v] + minn[u] + w);
            ans2 = max(ans2,maxx[v] + maxx[u] + w);
            minn[u] = min(minn[u],minn[v] + w);
            maxx[u] = max(maxx[u],maxx[v] + w);
            ans += w * sizee[v] * (k - sizee[v]);
        }
        head[u] = 0;
    }
    
    void solve()
    {
        ++Time;
        ans = 0;
        ans1 = inf;
        ans2 = -inf;
        scanf("%lld",&k);
        for (ll i = 1; i <= k; i++)
        {
            scanf("%lld",&a[i]),b[i] = a[i];
            flag[a[i]] = Time;
        }
        sort(a + 1,a + 1 + k,cmp);
        top = 0;
        tot = 1;
        sta[++top] = 1;
        for (ll i = 1; i <= k; i++)
        {
            ll LCA = lca(a[i],sta[top]);
            while (1)
            {
                if (deep[sta[top - 1]] <= deep[LCA])
                {
                    add2(LCA,sta[top]);
                    top--;
                    if (sta[top] != LCA)
                        sta[++top] = LCA;
                    break;
                }
                add2(sta[top - 1],sta[top]);
                top--;
            }
            if(sta[top] != a[i])
                sta[++top] = a[i];
        }
        top--;
        while (top)
        {
            add2(sta[top],sta[top + 1]);
            top--;
        }
        dp(1);
        printf("%lld %lld %lld
    ",ans,ans1,ans2);
    }
    
    int main()
    {
        scanf("%lld",&n);
        for (ll i = 1; i < n; i++)
        {
            ll x,y;
            scanf("%lld%lld",&x,&y);
            add(x,y);
            add(y,x);
        }
        dfs(1,0);
        for (ll j = 1; j <= 21; j++)
            for (ll i = 1; i <= n; i++)
                fa[i][j] = fa[fa[i][j - 1]][j - 1];
        memset(head,0,sizeof(head));
        tot = 1;
        scanf("%lld",&Q);
        while (Q--)
            solve();
    
        return 0;
    }
  • 相关阅读:
    国外插画等形式美术网址
    jquery对联广告
    国外优秀PS网站
    web前端开发工程师
    有关HTML+CSS需注意的一些问题
    公积金账户余额
    什么是CSS Float?
    5款影楼后期制作软件
    国外优秀设计网站推荐
    jQuery跟随屏幕滚动的层
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8503474.html
Copyright © 2020-2023  润新知