Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/AMS-Regular.js

BZOJ3611: [Heoi2014]大工程

BZOJ3611: [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 

题解Here!

看到k2×106k2×106就已经确定是在虚树上跑DPDP了。。。
不知道虚树的可以看这个:

虚树学习笔记

然后看看DPDP怎么搞。

第一问显然是直接对每一条边计算它的贡献。

num[x]num[x]表示xx的子树内有多少个选中的点,一共有mm个选中的点。

于是每一条边会被num[x]×(mnum[x])num[x]×(mnum[x])条路径覆盖。

为什么?

因为我们相当于从num[x],x的子树中选一个点,再在mnum[x],x的子树外选一个点。

然后把所有的贡献加起来就是答案。

记得开logn long

对于二、三两问,其实他俩的道理是类似的。

f[x]表示在x的子树内距离x最近的选中的点到x的距离,g[x]表示在x的子树内距离x最远的选中的点到x的距离。

维护长这个样:

f[x]=min{ f[v]+dis(x,v) | vsonx }

g[x]=max{ g[v]+dis(x,v) | vsonx }

答案怎么更新呢?

其实很简单,如果在x的子树中已经遍历过的点中有选中的点,则更新答案:

Ans_min=min{ f[x]+dix(x,v)+f[v] | vsonx }

Ans_max=max{ g[x]+dix(x,v)+g[v] | vsonx }

于是这两个问题也解决了。

LCA的话,树剖就好。

剩下的不多说,可以看代码。

附代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 1000010
#define MAX 999999999
using namespace std;
int n,m,q,c=1,d=1,e=1;
int head_a[MAXN],head_b[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],id[MAXN],top[MAXN];
int top_stack,minn,maxn,maxi,h[MAXN],stack[MAXN],f[MAXN],g[MAXN],num[MAXN];
long long sum;
bool choose[MAXN];
struct Tree{
    int next,to;
}a[MAXN<<1];
struct New_Tree{
    int next,to,w;
}b[MAXN<<1];
inline int read(){
    int date=0,w=1;char c=0;
    while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    return date*w;
}
inline bool cmp(const int &p,const int &q){
    return id[p]<id[q];
}
inline void add_a(int x,int y){
    a[c].to=y;a[c].next=head_a[x];head_a[x]=c++;
    a[c].to=x;a[c].next=head_a[y];head_a[y]=c++;
}
inline void add_b(int u,int v,int w){
    b[e].to=v;b[e].w=w;b[e].next=head_b[u];head_b[u]=e++;
}
void dfs1(int rt){
    son[rt]=0;size[rt]=1;
    for(int i=head_a[rt];i;i=a[i].next){
        int will=a[i].to;
        if(!deep[will]){
            deep[will]=deep[rt]+1;
            fa[will]=rt;
            dfs1(will);
            size[rt]+=size[will];
            if(size[will]>size[son[rt]])son[rt]=will;
        }
    }
}
void dfs2(int rt,int f){
    id[rt]=d++;top[rt]=f;
    if(son[rt])dfs2(son[rt],f);
    for(int i=head_a[rt];i;i=a[i].next){
        int will=a[i].to;
        if(will!=fa[rt]&&will!=son[rt])dfs2(will,will);
    }
}
int LCA(int x,int y){
    while(top[x]!=top[y]){
        if(deep[top[x]]<deep[top[y]])swap(x,y);
        x=fa[top[x]];
    }
    if(deep[x]>deep[y])swap(x,y);
    return x;
}
void rebuild(){
    int x,dis,lca;
    top_stack=1;
    stack[top_stack]=1;
    sort(h+1,h+m+1,cmp);
    for(int i=1;i<=m;i++){
        choose[h[i]]=true;
        if(h[i]==1)continue;
        x=h[i];
        lca=LCA(x,stack[top_stack]);
        while(top_stack>1&&deep[stack[top_stack-1]]>deep[lca]){
            dis=deep[stack[top_stack]]-deep[stack[top_stack-1]];
            add_b(stack[top_stack-1],stack[top_stack],dis);
            stack[top_stack--]=0;
        }
        if(deep[lca]<deep[stack[top_stack]]){
            dis=deep[stack[top_stack]]-deep[lca];
            add_b(lca,stack[top_stack],dis);
            stack[top_stack--]=0;
        }
        if(deep[lca]>deep[stack[top_stack]])stack[++top_stack]=lca;
        stack[++top_stack]=x;
    }
    while(top_stack>1){
        dis=deep[stack[top_stack]]-deep[stack[top_stack-1]];
        add_b(stack[top_stack-1],stack[top_stack],dis);
        stack[top_stack--]=0;
    }
}
void solve(int rt){
    num[rt]=choose[rt];g[rt]=0;f[rt]=(choose[rt]?0:MAX);
    int will,w;
    for(int i=head_b[rt];i;i=b[i].next)solve(b[i].to);
    for(int i=head_b[rt];i;i=b[i].next){
        will=b[i].to;w=b[i].w;
        sum+=1LL*(m-num[will])*num[will]*w;
        if(num[rt]){
            minn=min(minn,f[rt]+w+f[will]);
            maxn=max(maxn,g[rt]+w+g[will]);
        }
        f[rt]=min(f[rt],f[will]+w);
        g[rt]=max(g[rt],g[will]+w);
        num[rt]+=num[will];
    }
    head_b[rt]=0;choose[rt]=false;
}
void work(){
    while(q--){
        e=1;sum=maxn=0;minn=MAX;
        m=read();
        for(int i=1;i<=m;i++)h[i]=read();
        rebuild();
        solve(1);
        printf("%lld %d %d\n",sum,minn,maxn);
    }
}
void init(){
    int x,y;
    n=read();
    for(int i=1;i<n;i++){
        x=read();y=read();
        add_a(x,y);
    }
    q=read();
    deep[1]=1;
    dfs1(1);
    dfs2(1,1);
}
int main(){
    init();
    work();
    return 0;
}

 

posted @   符拉迪沃斯托克  阅读(191)  评论(0编辑  收藏  举报
编辑推荐:
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
阅读排行:
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· 软件产品开发中常见的10个问题及处理方法
· Vite CVE-2025-30208 安全漏洞
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(四):结合BotSharp
· MQ 如何保证数据一致性?
Live2D
欢迎阅读『BZOJ3611: [Heoi2014]大工程』
点击右上角即可分享
微信分享提示