3611: [Heoi2014]大工程
Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 1697 Solved: 718
[ 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
把所有的点拉出来建棵虚树
另一种建树步骤:
①求出所有点的dfs序和深度d[],并对整棵树进行倍增LCA的预处理
②对于每次查询,用一个栈维护,先让根进栈,之后按照dfs序遍历当次查询的所有点
③对于当前点x,如果栈顶节点y是x的祖先,x进栈
④对于当前点x,如果栈顶节点y不是x的祖先,不停地弹栈并连边,直到栈顶两个元素y和y'满足y'不是y的祖先
⑤将lca与栈顶y连边(前提lca!=y)弹出栈顶,x进栈,继续执行步骤③
⑥所有点处理完毕之后栈中剩下的依次连边成一条链,搞定
之后就是简单的树形DP了
t[x]:节点x是否是本次查询的点(1or0)
siz[x]:以节点x为根的子树中要查询的点有多少个
mn[x]:与离x节点最近的查询点的距离
mx[x]:与离x节点最远的查询点的距离
#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
#define LL long long
vector<int> G[1000005], G2[1000005];
int Time, dfn[1000005], d[1000005], f[1000005][24], siz[1000005];
int n, m, ansl, ansr, h[1000005], t[1000005], st[1000005], mx[1000005], mn[1000005];
LL ans;
bool comp(int x, int y)
{
if(dfn[x]<dfn[y])
return 1;
return 0;
}
void Sech(int u)
{
int i, v, j;
dfn[u] = ++Time;
for(i=0;i<G[u].size();i++)
{
v = G[u][i];
if(v==f[u][0])
continue;
f[v][0] = u;
for(j=0;f[f[v][j]][j]!=0;j++)
f[v][j+1] = f[f[v][j]][j];
d[v] = d[u]+1;
Sech(v);
}
}
int LCA(int u, int v)
{
int i;
if(d[u]<d[v])
swap(u, v);
for(i=19;i>=0;i--)
{
if(d[f[u][i]]>=d[v])
u = f[u][i];
}
if(u==v)
return u;
for(i=19;i>=0;i--)
{
if(f[u][i]!=f[v][i])
u = f[u][i], v = f[v][i];
}
return f[u][0];
}
void DP(int u)
{
int v, i;
siz[u] = t[u];
if(t[u])
mx[u] = mn[u] = 0;
else
mx[u] = -2*n, mn[u] = 2*n;
for(i=0;i<G2[u].size();i++)
{
v = G2[u][i];
DP(v);
siz[u] += siz[v];
ans += (LL)siz[v]*(m-siz[v])*(d[v]-d[u]);
ansl = max(ansl, mx[u]+mx[v]+d[v]-d[u]);
ansr = min(ansr, mn[u]+mn[v]+d[v]-d[u]);
mx[u] = max(mx[u], mx[v]+d[v]-d[u]);
mn[u] = min(mn[u], mn[v]+d[v]-d[u]);
}
}
int main(void)
{
int i, x, y, q, cnt, top, lca;
scanf("%d", &n);
for(i=1;i<=n-1;i++)
{
scanf("%d%d", &x, &y);
G[x].push_back(y);
G[y].push_back(x);
}
d[1] = 1;
Sech(1);
scanf("%d", &q);
while(q--)
{
scanf("%d", &m);
for(i=1;i<=m;i++)
{
scanf("%d", &h[i]);
t[h[i]] = 1;
}
if(m==1)
{
printf("0 0 0\n");
continue;
}
sort(h+1, h+m+1, comp);
top = 0, cnt = m;
st[++top] = 1;
for(i=1;i<=m;i++)
{
if(h[i]==1)
continue;
lca = LCA(st[top], h[i]);
if(lca==st[top])
{
st[++top] = h[i];
continue;
}
while(LCA(st[top-1], h[i])==lca)
{
G2[st[top-1]].push_back(st[top]);
top--;
}
if(lca!=st[top])
{
h[++cnt] = lca;
G2[lca].push_back(st[top]);
}
st[top] = lca;
st[++top] = h[i];
}
while(top--)
G2[st[top]].push_back(st[top+1]);
ans = 0, ansl = -2*n, ansr = 2*n;
DP(1);
printf("%lld %d %d\n", ans, ansr, ansl);
for(i=1;i<=cnt;i++)
t[h[i]] = 0;
G2[1].clear();
for(i=1;i<=cnt;i++)
G2[h[i]].clear();
}
return 0;
}
/*
18
1 2
2 3
2 4
2 7
7 8
7 9
4 5
5 6
1 10
10 11
10 12
10 13
12 14
12 15
14 16
14 17
17 18
1
7
7 5 11 12 13 16 18
*/