[HEOI2014]大工程
題目描述
國家有一個大工程,要給一個非常大的交通網絡裏建一些新的通道。
我們這個國家位置非常特殊,可以看成是一個單位邊權的樹,城市位於頂點上。
在 2 個國家 a,b 之間建一條新通道需要的代價為樹上 a,b 的最短路徑。
現在國家有很多個計劃,每個計劃都是這樣,我們選中了 k 個點,然後在它們兩兩之間 新建 C(k,2)條 新通道。現在對於每個計劃,我們想知道: 1.這些新通道的代價和 2.這些新通道中代價最小的是多少 3.這些新通道中代價最大的是多少
輸入輸出格式
輸入格式:
第一行 n 表示點數。
接下來 n-1 行,每行兩個數 a,b 表示 a 和 b 之間有一條邊。點從 1 開始標號。
接下來一行 q 表示計劃數。對每個計劃有 2 行,第一行 k 表示這個計劃選中了幾個點。
第二行用空格隔開的 k 個互不相同的數表示選了哪 k 個點。
輸出格式:
輸出 q 行,每行三個數分別表示代價和,最小代價,最大代價。
輸入輸出樣例
輸入樣例#1: 復制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輸出樣例#1: 復制
3 3 3 6 6 6 1 1 1 2 2 2 2 2 2
說明
對於第 1,2 個點: n<=10000
對於第 3,4,5 個點: n<=100000,交通網絡構成一條鏈
對於第 6,7 個點: n<=100000
對於第 8,9,10 個點: n<=1000000
對於所有數據, q<=50000並且保證所有k之和<=2*n
看到k的和小於2*n,於是立刻想到建虛樹
建出虛樹後就dp
size[x]表示x的子樹中關鍵點數
f[x]表示x子樹中路徑的貢獻和
Min[x]表示x子樹中離x距離最小的關鍵點的距離
Max[x]表示最大的距離
最大值和求和很簡單
最大值總是要取到葉子節點,虛樹中葉子節點總是關鍵點
答案取當前Max[x]+Max[v]+邊權w,然後Max[x]=max(Max[x],Max[v]+d)
求和就考慮一條邊的貢獻
一條邊的貢獻次數顯然是size[v]*(k-size[v])
所以f[x]+=f[v]+size[v]*(k-size[v])*w
求最小值的話,Min[x]初值正無窮
如果是關鍵點就直接取它的子樹路徑最小值,否則就是它的兩個兒子的子樹路徑最小值相加
如果是關鍵點,更新答案後Min[x]要清0,作為接下來的端點
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 typedef long long lol; 8 const int N=2000005; 9 struct Node 10 { 11 int next,to; 12 }edge[N],edge2[N]; 13 int inf=1e9; 14 int dep[N],fa[N][21],dfn[N],cnt,bin[25],head[N],head2[N],num,ed[N]; 15 int size[N],vis[N],Max[N],Min[N],k,M,ans1,ans2,n,Lca,a[N],s[N],top; 16 lol f[N]; 17 int gi() 18 { 19 char ch=getchar(); 20 int x=0; 21 while (ch<‘0‘||ch>‘9‘) ch=getchar(); 22 while (ch>=‘0‘&&ch<=‘9‘) 23 { 24 x=x*10+ch-‘0‘; 25 ch=getchar(); 26 } 27 return x; 28 } 29 bool cmp(int a,int b) 30 { 31 return dfn[a]<dfn[b]; 32 } 33 void add(int u,int v) 34 { 35 num++; 36 edge[num].next=head[u]; 37 head[u]=num; 38 edge[num].to=v; 39 } 40 void add2(int u,int v) 41 { 42 if (u==v) return; 43 num++; 44 edge2[num].next=head2[u]; 45 head2[u]=num; 46 edge2[num].to=v; 47 } 48 void dfs(int x,int pa) 49 {int i; 50 dep[x]=dep[pa]+1; 51 dfn[x]=++cnt; 52 for (i=1;bin[i]<=dep[x];i++) 53 fa[x][i]=fa[fa[x][i-1]][i-1]; 54 for (i=head[x];i;i=edge[i].next) 55 { 56 int v=edge[i].to; 57 if (v==pa) continue; 58 fa[v][0]=x; 59 dfs(v,x); 60 } 61 ed[x]=cnt; 62 } 63 int lca(int x,int y) 64 {int as,i; 65 if (dep[x]<dep[y]) swap(x,y); 66 for (i=20;i>=0;i--) 67 if (bin[i]<=dep[x]-dep[y]) 68 x=fa[x][i]; 69 if (x==y) return x; 70 for (i=20;i>=0;i--) 71 { 72 if (fa[x][i]!=fa[y][i]) 73 { 74 x=fa[x][i];y=fa[y][i]; 75 } 76 } 77 return fa[x][0]; 78 } 79 80 void dp(int x) 81 {int i; 82 size[x]=vis[x]; 83 Max[x]=0;Min[x]=inf;f[x]=0; 84 for (i=head2[x];i;i=edge2[i].next) 85 { 86 int v=edge2[i].to,d=dep[v]-dep[x]; 87 dp(v); 88 size[x]+=size[v]; 89 f[x]+=f[v]+1ll*size[v]*(k-size[v])*d; 90 ans1=min(ans1,Min[x]+Min[v]+d);Min[x]=min(Min[x],Min[v]+d); 91 ans2=max(ans2,Max[x]+Max[v]+d);Max[x]=max(Max[x],Max[v]+d); 92 } 93 if (vis[x]) ans1=min(ans1,Min[x]),ans2=max(ans2,Max[x]),Min[x]=0; 94 } 95 int main() 96 {int i,u,v,j,q; 97 cin>>n; 98 bin[0]=1; 99 for (i=1;i<=20;i++) 100 bin[i]=bin[i-1]*2; 101 for (i=1;i<=n-1;i++) 102 { 103 scanf("%d%d",&u,&v); 104 add(u,v);add(v,u); 105 } 106 dfs(1,0); 107 cin>>q; 108 while (q--) 109 { 110 k=gi(); 111 M=k; 112 num=0;ans1=inf;ans2=0; 113 for (i=1;i<=k;i++) 114 a[i]=gi(),vis[a[i]]=1; 115 sort(a+1,a+k+1,cmp); 116 Lca=a[1]; 117 for (i=2;i<=k;i++) 118 if (ed[a[i-1]]<dfn[a[i]]) 119 a[++M]=lca(a[i-1],a[i]),Lca=lca(Lca,a[i]); 120 a[++M]=Lca; 121 sort(a+1,a+M+1,cmp); 122 M=unique(a+1,a+M+1)-a-1; 123 s[++top]=a[1]; 124 for (i=2;i<=M;i++) 125 { 126 while (top&&ed[s[top]]<dfn[a[i]]) top--; 127 add2(s[top],a[i]); 128 s[++top]=a[i]; 129 } 130 dp(Lca); 131 printf("%lld %d %d\n",f[Lca],ans1,ans2); 132 for (i=1;i<=M;i++) 133 vis[a[i]]=head2[a[i]]=0; 134 } 135 }
[HEOI2014]大工程