zoukankan      html  css  js  c++  java
  • BZOJ3611 [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 行,每行三个数分别表示代价和,最小代价,最大代价。

    输入样例

    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

    输出样例

    3 3 3

    6 6 6

    1 1 1

    2 2 2

    2 2 2

    提示

    n<=1000000

    q<=50000并且保证所有k之和<=2*n

    题解

    好久没写虚树了,一写整个人就虚完了,

    这题没什么难点,建完虚树后比较基础的树形dp就完了

    最大值就维护当前子树最大值,然后尝试将一个子树最大值与当前子树中最大值相加更新答案
    最小值类似
    总和呢,考虑一个子树内的所有点要往上走,都要经过这条边,那么有(siz[son] * (k - siz[son]))种组合,乘以边长作为贡献

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define LL long long int
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
    using namespace std;
    const int maxn = 1000005,maxm = 2000005,INF = 1000000000;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    	return out * flag;
    }
    int hh[maxn],nn = 2;
    int h[maxn],ne = 2,de[maxn];
    struct EDGE{int to,nxt,w;}e[maxm],ed[maxm];
    inline void add(int u,int v){
    	e[nn] = (EDGE){v,hh[u],0}; hh[u] = nn++;
    	e[nn] = (EDGE){u,hh[v],0}; hh[v] = nn++;
    }
    inline void build(int u,int v,int w){
    	ed[ne] = (EDGE){v,h[u],w}; h[u] = ne++;
    	ed[ne] = (EDGE){u,h[v],w}; h[v] = ne++;
    	de[u]++; de[v]++;
    }
    int n,K,a[maxn],fa[maxn][21],dep[maxn],dfn[maxn],cnt;
    void dfs(int u){
    	dfn[u] = ++cnt;
    	for (int i = 1; i <= 20; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1];
    	for (int k = hh[u],to; k; k = e[k].nxt)
    		if ((to = e[k].to) != fa[u][0]){
    			fa[to][0] = u;
    			dep[to] = dep[u] + 1;
    			dfs(to);
    		}
    }
    int lca(int u,int v){
    	if (dep[u] < dep[v]) swap(u,v);
    	for (int i = 0,d = dep[u] - dep[v]; (1 << i) <= d; i++)
    		if ((1 << i) & d) u = fa[u][i];
    	if (u == v) return u;
    	for (int i = 20; i >= 0; i--)
    		if (fa[u][i] != fa[v][i]){
    			u = fa[u][i];
    			v = fa[v][i];
    		}
    	return fa[u][0];
    }
    int st[maxn],top;
    inline bool cmp(const int& a,const int& b){
    	return dfn[a] < dfn[b];
    }
    void rebuild(){
    	top = 0; ne = 2;
    	sort(a + 1,a + 1 + K,cmp);
    	st[++top] = 1;
    	for (int i = 1; i <= K; i++){
    		int u = a[i],v = lca(u,st[top]);
    		if (v == st[top]) st[++top] = u;
    		else {
    			while (true){
    				if (dep[v] >= dep[st[top - 1]]){
    					build(v,st[top],dep[st[top]] - dep[v]);
    					top--;
    					st[++top] = v;
    					break;
    				}
    			 	build(st[top],st[top - 1],dep[st[top]] - dep[st[top - 1]]);
    				top--;
    			}
    			st[++top] = u;
    		}
    	}
    	while (top > 1) build(st[top],st[top - 1],dep[st[top]] - dep[st[top - 1]]),top--;
    }
    int tag[maxn];
    LL sum,gmax,gmin,mn[maxn],mx[maxn],siz[maxn];
    void DFS(int u){
    	mx[u] = tag[u] ? 0 : -INF;
    	mn[u] = tag[u] ? 0 : INF;
    	siz[u] = tag[u];
    	Redge(u) if (dep[to = ed[k].to] > dep[u]){
    		DFS(to);
    		sum += siz[to] * (K - siz[to]) * ed[k].w;
    		siz[u] += siz[to];
    		gmin = min(gmin,mn[u] + mn[to] + ed[k].w);
    		gmax = max(gmax,mx[u] + mx[to] + ed[k].w);
    		mn[u] = min(mn[u],mn[to] + ed[k].w);
    		mx[u] = max(mx[u],mx[to] + ed[k].w);
    	}
    	h[u] = de[u] = 0;
    }
    void solve(){
    	rebuild();
    	for (int i = 1; i <= K; i++) tag[a[i]] = 1;
    	sum = gmax = 0;
    	gmin = INF;
    	if (de[1] == 1 && !tag[1]){
    		DFS(ed[h[1]].to);
    		h[1] = de[1] = 0;
    	}
    	else DFS(1);
    	printf("%lld %lld %lld
    ",sum,gmin,gmax);
    	for (int i = 1; i <= K; i++) tag[a[i]] = 0;
    }
    int main(){
    	n = read();
    	for (int i = 1; i < n; i++) add(read(),read());
    	dfs(1);
    	int q = read();
    	while (q--){
    		K = read();
    		for (int i = 1; i <= K; i++) a[i] = read();
    		solve();
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    7.Perfect Number
    6.Hamming Distance
    5.Palindrome Number
    4.String to Integer (atoi)
    3.Reverse Integer
    [HP SIM] Systems Insight Manager stopped working, sqlserver error code 0x80090302。
    [HP SIM] Systems Insight Manager 不能正常工作,数据库错误0x80090302。
    [Outlook] 用powershell做outlook中的delegate.
    [Outlook] Use powershell to do delegates like outlook.
    [Outlook] profile在注册表里的秘密。
  • 原文地址:https://www.cnblogs.com/Mychael/p/8666750.html
Copyright © 2011-2022 走看看