• 【XSY1977】【BZOJ3611】【HEOI2014】大工程(虚树+dp)


    (Description)

    国家有一个大工程,要给一个非常大的交通网络里建一些新的通道。

    我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上。

    (2) 个国家 (a,b) 之间建一条新通道需要的代价为树上 (a,b) 的最短路径。

    现在国家有很多个计划,每个计划都是这样,我们选中了 (k) 个点,然后在它们两两之间 新建 (C(2,k))条 新通道。

    现在对于每个计划,我们想知道:

    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


    (HINT)

    (n≤1000000)
    (q≤50000,∑ki≤2×n)


    (Source)

     练习题 树8-5-虚树
    

    思路

    首先我们考虑(dp)

    我们设三个数组(sum[u]),(mn[u]),(mx[u])

    分别表示,对于以(u)为根的子树中的代价总和,最小值和最大值

    (u)到当前遍历到的儿子(v)的代价为(cost)

    那么我们轻易得出(dp)方程:

    (egin{cases}mn[u]=min(mn[u],mn[v]+cost)\mx[u]=max(mx[u],mx[v]+cost)\sum[u]+=sum[v]+siz[v] imes costend{cases})

    同时,对于答案的更新就很显然了:

    (egin{cases}ans1+=siz[u] imes (sum[v]+cost*siz[v])+siz[v] imes sum[u]\ans2=min(ans2,mn[u]+mn[v]+cost)\ans3=max(ans3,mx[u]+mx[v]+cost)end{cases})

    以上的如果不懂可以自己手画图来推下,容易理解

    但是,我们来瞅一眼时间复杂度:在这种情况下,时间复杂度是(O(qn))

    是的你没看错,绝对会爆


    正当我们一筹莫展的时候,仔细看一遍题面会对我们有极大帮助

    我们一眼看到 (∑ki≤2×n) ,果断虚树


    关于不会虚树的小白,请参考自为风月马前卒大佬的博客

    学会建虚树后,我们直接开始在虚树上(dp)就好了

    注意:

    1. 要开 (long long)
    2. 在虚树上 (dp) 时,不要直接使用原树中处理出的(siz),毕竟虚树的(siz)跟原树中的(siz)还是有很大区别的
    3. 因为在虚树中(dp)时,对于关键点和非关键点的处理是不同的:
      如果是关键点, 那么(mn[u]=mx[u]=0,sz[u]=1)
      如果不是,那么(mn[u]=INF,mx[u]=-INF,sz[u]=0)

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1000010;
    const long long INF=0x7ffffffffff;
    int n,k,q,cnt=0,tot=0,ind=0;
    int to[N<<1],nxt[N<<1],head[N],w[N<<1];
    int p[N];
    int fa[N];
    int st[N];
    int d[N];
    int dfn[N];
    int son[N];
    int topp[N];
    int siz[N];
    bool vis[N];
    void add(int u,int v)
    {
    	to[++cnt]=v;
    	nxt[cnt]=head[u];
    	head[u]=cnt;
    }
    void add2(int u,int v)
    {
    	to[++cnt]=v;
    	nxt[cnt]=head[u];
    	w[cnt]=d[v]-d[u];
    	head[u]=cnt;
    }
    void dfs1(int u)
    {
    	siz[u]=1;
    	for(int i=head[u];i;i=nxt[i])
    	{
    		int v=to[i];
    		if(v==fa[u])continue;
    		d[v]=d[u]+1;
    		fa[v]=u;
    		dfs1(v);
    		siz[u]+=siz[v];
    		if(siz[v]>siz[son[u]])son[u]=v;
    	}
    }
    void dfs2(int u,int tp)
    {
    	topp[u]=tp;
    	dfn[u]=++ind;
    	if(son[u])dfs2(son[u],tp);
    	for(int i=head[u];i;i=nxt[i])
    	{
    		int v=to[i];
    		if(v==fa[u]||v==son[u])continue;
    		dfs2(v,v);
    	}
    }
    int query_lca(int x,int y)
    {
    	while(topp[x]!=topp[y])
    	{
    		if(d[topp[x]]<d[topp[y]])swap(x,y);
    		x=fa[topp[x]];
    	}
    	if(d[x]>d[y])return y;
    	return x;
    }
    bool cmp(int x,int y)
    {
    	return dfn[x]<dfn[y];
    }
    void ins(int x)
    {
    	if(tot==1)
    	{
    		st[++tot]=x;
    		return ;
    	}
    	int lca=query_lca(st[tot],x);
    	while(tot>1&&dfn[st[tot-1]]>=dfn[lca])add2(st[tot-1],st[tot]),tot--;
    	if(lca!=st[tot])add2(lca,st[tot]),st[tot]=lca;
    	st[++tot]=x;
    }
    long long ans1,ans2,ans3;
    long long sum[N],mn[N],mx[N];
    int sz[N];
    void dp(int u)
    {
    	sum[u]=0;
    	if(vis[u])mn[u]=mx[u]=0,sz[u]=1;
    	else mn[u]=INF,mx[u]=-INF,sz[u]=0;
    	for(int i=head[u];i;i=nxt[i])
    	{
    		int cost=w[i],v=to[i];
    		dp(v);
    		ans2=min(ans2,mn[u]+mn[v]+cost);
    		ans3=max(ans3,mx[u]+mx[v]+cost);
    		mn[u]=min(mn[u],mn[v]+cost);
    		mx[u]=max(mx[u],mx[v]+cost);
    		ans1+=1ll*sz[u]*(sum[v]+cost*sz[v])+1ll*sz[v]*sum[u];
    		sum[u]+=sum[v]+sz[v]*cost;
    		sz[u]+=sz[v];
    	}
    	vis[u]=0,head[u]=0;
    }
    int main()
    {
    	scanf("%d",&n);
    	int a,b;
    	for(int i=1;i<n;i++)
    	{
    		scanf("%d %d",&a,&b);
    		add(a,b),add(b,a);
    	}
    	d[1]=1;
    	dfs1(1);
    	dfs2(1,1);
    	scanf("%d",&q);
    	memset(head,0,sizeof(head));
    	while(q--)
    	{
    		cnt=0;
    		scanf("%d",&k);
    		for(int i=1;i<=k;i++)scanf("%d",&p[i]),vis[p[i]]=1;
    		sort(p+1,p+k+1,cmp);
    		if(p[1]!=1)st[tot=1]=1;
    		for(int i=1;i<=k;i++)ins(p[i]);
    		while(tot)add2(st[tot-1],st[tot]),tot--;
    		ans1=0ll,ans2=INF,ans3=-INF;
    		dp(1);
    		printf("%lld %lld %lld
    ",ans1,ans2,ans3);
    	}
    	return 0;
    }
    /*
    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
    */
    
  • 相关阅读:
    一起来学linux:网络命令
    python网络爬虫之使用scrapy自动登录网站
    python网络爬虫之requests库 二
    一起来学linux:网络配置
    一起来学linux:日志文件
    一起来学linux:进程
    python网络爬虫之requests库
    【原创】大数据基础之Ambari(1)简介、编译安装、使用
    【原创】数据库基础之Mysql(1)常用命令
    【原创】大叔经验分享(22)securecrt连接自动断开
  • 原文地址:https://www.cnblogs.com/ShuraEye/p/11960068.html
Copyright © 2020-2023  润新知