zoukankan      html  css  js  c++  java
  • 【BZOJ3611】[Heoi2014]大工程 欧拉序+ST表+单调栈

    【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

    Sample Output

    3 3 3
    6 6 6
    1 1 1
    2 2 2
    2 2 2

    HINT

    n<=1000000 

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

    题解:看到题时感觉不太难,到了对拍时发现网上题解都是虚树,然而蒟蒻没学过虚树啊,整个人都傻了。然而最后不用虚树还是做出来了~

    首先由这样一个性质:两个点的LCA的深度,就是在欧拉序中,两个点间的所有点的深度的最小值。即:我们用ST表维护欧拉序中每个点的深度,求出两个位置中的深度最小值,就是两点LCA的深度。

    那么,本题求的是所有点对的LCA的深度,那么我们不妨考虑每个LCA的深度对答案做的贡献。我们将k个点按照欧拉序排序,然后求出相邻两点之间的深度最小值,记为vj。我们考虑vj对答案做的贡献,即vj能成为那些点对的LCA的深度,即vj能成为那些区间中深度的最小值。显然可以用单调栈搞定。我们用单调栈求出每个vj左(右)边第一个比它大的v的位置,即为lp和rp。那么vj可以成为:一个点在[lp,j]中,一个点为[j+1,rp]中,所有这样的点对的LCA的深度。那么对于第一问,我们再维护一下深度的前缀和就搞定了。

    那么第二问怎么办?根据贪心的思想,如果我们已经确定了vj,那么最优的点对一定满足:一个点是[lp,j]中深度最小的,一个点时[j+1,rp]中深度最小的。这就变成了RMQ问题,依旧上ST表。(本人坚信ST表的常数。)第三问同理。

    细节:

    1.为了防止重复,我们要稍微改变一下vj的定义。在排完序后的k个点中,设第j个点在欧拉序中的位置是pj,那么vj其实是[pj,pj+1)这段区间的深度最小值。你可能担心我们将最后一个点遗漏了,但是你会发现,欧拉序排在最后的点一定是不会成为任何点对的LCA的。所以不需要额外计算。
    2.依旧为了防止重复,我们还要稍微改变一下lp,rp的定义。lp[j]:j左边,第一个<v[j]的位置;rp[j]:j右边,第一个>=v[j]的位置。特别地,如果不存在这样的位置,则令lp=0,rp=m。

    其实这种做法跟虚树的思想差不多吧~

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    const int maxn=1000010;
    typedef long long ll;
    const int inf=1<<30;
    ll sum;
    int n,m,cnt,q,top,maxx,minn;
    int dep[maxn],md[22][maxn<<1],p[maxn],to[maxn<<1],next[maxn<<1],head[maxn],Log[maxn<<1],pos[maxn];
    int st[maxn],lp[maxn],rp[maxn],v[maxn],sn[22][maxn],sm[22][maxn];
    ll s[maxn];
    int rd()
    {
    	int ret=0;	char gc=getchar();
    	while(gc<'0'||gc>'9')	gc=getchar();
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret;
    }
    void add(int a,int b)
    {
    	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
    }
    void dfs(int x)
    {
    	pos[x]=++pos[0],md[0][pos[0]]=dep[x];
    	for(int i=head[x];i!=-1;i=next[i])	if(!dep[to[i]])	dep[to[i]]=dep[x]+1,dfs(to[i]),md[0][++pos[0]]=dep[x];
    }
    int getmin(int a,int b)
    {
    	int k=Log[b-a+1];
    	return min(md[k][a],md[k][b-(1<<k)+1]);
    }
    int qm(int a,int b)
    {
    	int k=Log[b-a+1];
    	return max(sm[k][a],sm[k][b-(1<<k)+1]);
    }
    int qn(int a,int b)
    {
    	int k=Log[b-a+1];
    	return min(sn[k][a],sn[k][b-(1<<k)+1]);
    }
    bool cmp(int a,int b)
    {
    	return pos[a]<pos[b];
    }
    int main()
    {
    	n=rd();
    	int i,j,k,a,b;
    	memset(head,-1,sizeof(head));
    	memset(sm,0x80,sizeof(sm));
    	memset(sn,0x3f,sizeof(sn));
    	memset(md,0x3f,sizeof(md));
    	for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b),add(b,a);
    	dep[1]=1,dfs(1);
    	for(i=2;i<=pos[0];i++)	Log[i]=Log[i>>1]+1;
    	for(j=1;(1<<j)<=pos[0];j++)	for(i=1;i+(1<<j)-1<=pos[0];i++)	md[j][i]=min(md[j-1][i],md[j-1][i+(1<<j-1)]);
    	q=rd();
    	for(i=1;i<=q;i++)
    	{
    		m=rd(),sum=0,maxx=-inf,minn=inf;
    		for(j=1;j<=m;j++)	p[j]=rd();
    		sort(p+1,p+m+1,cmp);
    		for(j=1;j<=m;j++)	s[j]=s[j-1]+dep[p[j]],sm[0][j]=sn[0][j]=dep[p[j]];
    		for(k=1;(1<<k)<=m;k++)	for(j=1;j+(1<<k)-1<=m;j++)
    		{
    			sm[k][j]=max(sm[k-1][j],sm[k-1][j+(1<<k-1)]);
    			sn[k][j]=min(sn[k-1][j],sn[k-1][j+(1<<k-1)]);
    		}
    		for(j=1;j<m;j++)	v[j]=getmin(pos[p[j]],pos[p[j+1]]-1);
    		for(top=0,j=1;j<m;j++)
    		{
    			while(top&&v[st[top]]>v[j])	top--;
    			lp[j]=(top)?st[top]:0,st[++top]=j;
    		}
    		for(top=0,j=m-1;j;j--)
    		{
    			while(top&&v[st[top]]>=v[j])	top--;
    			rp[j]=(top)?st[top]:m,st[++top]=j;
    		}
    		for(j=1;j<m;j++)
    		{
    			sum-=(ll)(j-lp[j])*(rp[j]-j)*(2*v[j]);
    			sum+=(ll)(s[j]-s[lp[j]])*(rp[j]-j);
    			sum+=(ll)(s[rp[j]]-s[j])*(j-lp[j]);
    			maxx=max(maxx,qm(lp[j]+1,j)+qm(j+1,rp[j])-2*v[j]);
    			minn=min(minn,qn(lp[j]+1,j)+qn(j+1,rp[j])-2*v[j]);
    		}
    		for(j=1;j<=m;j++)	sm[0][j]=-inf,sn[0][j]=inf;
    		printf("%lld %d %d
    ",sum,minn,maxx);
    	}
    	return 0;
    }
  • 相关阅读:
    (07)使用WireMock快速伪造REST服务
    (06)使用Swagger自动生成html文档,描述API接口
    (05)使用DeferredResult多线程异步处理请求
    (04)Spring开发中的3种拦截机制
    (03)使用SpringBoot自定义Restful风格异常处理,返回json格式数据
    (02)Restful风格的增删改查、上传、下载案例及其junit测试详解
    (01)Restful简介
    (03)maven项目分模块开发,子项目继承自父项目,打包运行方法
    (018)Spring Boot之常用配置
    (031)Spring Boot之服务的注册与发现,使用zookeeper演示负载均衡
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7148915.html
Copyright © 2011-2022 走看看