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
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
6 6 6
1 1 1
2 2 2
2 2 2
HINT
n<=1000000
q<=50000并且保证所有k之和<=2*n
题解Here!
看到∑k≤2×106∑k≤2×106就已经确定是在虚树上跑DPDP了。。。
不知道虚树的可以看这个:
虚树学习笔记
然后看看DPDP怎么搞。
第一问显然是直接对每一条边计算它的贡献。
设num[x]num[x]表示xx的子树内有多少个选中的点,一共有mm个选中的点。
于是每一条边会被num[x]×(m−num[x])num[x]×(m−num[x])条路径覆盖。
为什么?
因为我们相当于从num[x],即x的子树中选一个点,再在m−num[x],即x的子树外选一个点。
然后把所有的贡献加起来就是答案。
记得开logn long。
对于二、三两问,其实他俩的道理是类似的。
设f[x]表示在x的子树内距离x最近的选中的点到x的距离,g[x]表示在x的子树内距离x最远的选中的点到x的距离。
维护长这个样:
f[x]=min{ f[v]+dis(x,v) | v∈sonx }
g[x]=max{ g[v]+dis(x,v) | v∈sonx }
答案怎么更新呢?
其实很简单,如果在x的子树中已经遍历过的点中有选中的点,则更新答案:
Ans_min=min{ f[x]+dix(x,v)+f[v] | v∈sonx }
Ans_max=max{ g[x]+dix(x,v)+g[v] | v∈sonx }
于是这两个问题也解决了。
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; } |
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· 软件产品开发中常见的10个问题及处理方法
· Vite CVE-2025-30208 安全漏洞
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(四):结合BotSharp
· MQ 如何保证数据一致性?