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\n",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;
}

 

posted @ 2018-03-03 23:19  zbtrs  阅读(247)  评论(0编辑  收藏  举报