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
正解:虚树DP
解题报告:题目让我们求三个东西,求和的话我们可以把∑每条边两边的关键点(关键点就是这一次给出的点)的数量相乘,最短的路的话我们可以记录一下从当前这个节点到任意一个关键点的最短距离(如果当前这个节点是关键点,那最短距离为0),然后就可以DP做,最长路是一样的,然而,如果每次查询都在原树上做显然会T飞,所以我们可以用一种神奇的数据结构-----虚树,就是把跟这一次查询有关的点新建一棵树,如果不懂可以去网上查资料
#include <iostream> #include <iomanip> #include <cstdlib> #include <cstdio> #include <cmath> #include <string> #include <cstring> #include <algorithm> #define int long long #define RG register const int N = 2000050; const int inf = 2147483641; using namespace std; int gi(){ char ch=getchar();int x=0; while(ch<'0' || ch>'9') ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return x; } int cnt,dis[N],siz[N],fa[N],son[N],id[N],top[N],g[N]; int nn[N*2][2],head[N],jl[N*2],st[N*2],ra[N],vis[N],dp[N]; int nx[N*2][3],tail[N],v,maxlen[N],k,t,u,mi,minlen[N]; long long ans; void dfs1(int x,int f){ dis[x]=dis[f]+1,fa[x]=f; for (RG int i=head[x]; i; i=nn[i][0]) if (nn[i][1]!=f){ dfs1(nn[i][1],x); if (siz[nn[i][1]]>siz[son[x]]) son[x]=nn[i][1]; siz[x]+=siz[nn[i][1]]; } ++siz[x]; return; } void dfs2(int x,int tp){ id[x]=++cnt,top[x]=tp,ra[cnt]=x; if (son[x]) dfs2(son[x],tp); for (RG int i=head[x]; i; i=nn[i][0]) if (nn[i][1]!=fa[x] && nn[i][1]!=son[x]) dfs2(nn[i][1],nn[i][1]); return; } int cmp(int a,int b){ return id[a]<id[b]; } int lca(int a,int b){ while(top[a]!=top[b]) if (dis[top[a]]>dis[top[b]]) a=fa[top[a]]; else b=fa[top[b]]; if (dis[a]>dis[b]) return b; return a; } inline int MAX(int a,int b){ return a>b?a:b; } inline int MIN(int a,int b){ return a<b?a:b; } void dfs(int x,int f){ dp[x]=maxlen[x]=0,siz[x]=0; minlen[x]=vis[x]==t?0:inf; for (RG int i=tail[x]; i; i=nx[i][0]) if (nx[i][1]!=f){ dfs(nx[i][1],x); siz[x]+=siz[nx[i][1]]; dp[x]=MAX(dp[nx[i][1]],MAX(dp[x],maxlen[x]+nx[i][2]+maxlen[nx[i][1]])); mi=MIN(mi,minlen[x]+minlen[nx[i][1]]+nx[i][2]); ans+=siz[nx[i][1]]*(u-siz[nx[i][1]])*nx[i][2]; maxlen[x]=MAX(maxlen[x],maxlen[nx[i][1]]+nx[i][2]); minlen[x]=MIN(minlen[x],minlen[nx[i][1]]+nx[i][2]); } siz[x]+=vis[x]==t?1:0; return; } main(){ int n=gi(); for (RG int i=1; i<n; ++i){ int l=gi(),r=gi(); nn[++cnt][1]=l,nn[cnt][0]=head[r],head[r]=cnt; nn[++cnt][1]=r,nn[cnt][0]=head[l],head[l]=cnt; } cnt=0,dfs1(1,0),dfs2(1,1); int q=gi(); for (RG int i=1; i<=q; ++i){ k=gi(),cnt=k,t=i,u=k,mi=inf; for (RG int j=1; j<=k; ++j) jl[j]=gi(),vis[jl[j]]=i; sort(jl+1,jl+k+1,cmp); for (RG int j=2; j<=k; ++j) jl[++cnt]=lca(jl[j],jl[j-1]); sort(jl+1,jl+cnt+1,cmp); k=unique(jl+1,jl+cnt+1)-jl-1; st[v=1]=jl[1],ans=cnt=0; for (RG int j=2; j<=k; ++j){ while(lca(st[v],jl[j])!=st[v]) --v; nx[++cnt][1]=st[v],nx[cnt][0]=tail[jl[j]],tail[jl[j]]=cnt,nx[cnt][2]=dis[jl[j]]-dis[st[v]]; nx[++cnt][1]=jl[j],nx[cnt][0]=tail[st[v]],tail[st[v]]=cnt,nx[cnt][2]=dis[jl[j]]-dis[st[v]]; st[++v]=jl[j]; } dfs(jl[1],0); printf("%lld %lld %lld\n",ans,mi,dp[jl[1]]); for (RG int j=1; j<=k; ++j) tail[jl[j]]=0; } return 0; }