NOIP2018 Day2T3 保卫王国 倍增

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baidu_36797646/article/details/84197604

题目描述:

Z 国有n座城市,n - 1条双向道路,每条双向道路连接两座城市,且任意两座城市 都能通过若干条道路相互到达。
Z 国的国防部长小 Z 要在城市中驻扎军队。驻扎军队需要满足如下几个条件:

一座城市可以驻扎一支军队,也可以不驻扎军队。
由道路直接连接的两座城市中至少要有一座城市驻扎军队。
在城市里驻扎军队会产生花费,在编号为i的城市中驻扎军队的花费是p_i。

小 Z 很快就规划出了一种驻扎军队的方案,使总花费最小。但是国王又给小 Z 提出 了m个要求,每个要求规定了其中两座城市是否驻扎军队。小 Z 需要针对每个要求逐一 给出回答。具体而言,如果国王提出的第j个要求能够满足上述驻扎条件(不需要考虑第j个要求之外的其它要求),则需要给出在此要求前提下驻扎军队的最小开销。如果国王提出的第j个要求无法满足,则需要输出-1 (1 ≤ j ≤ m)。现在请你来帮助小 Z。

题解:

我们先来考虑序列(也就是一条链)应该怎么做。显然两个特殊点把整个序列分为了三部分,而区间之间的信息也是很好合并的,很容易想到用线段树或者倍增做,对于每个区间记录 f [ 0 / 1 ] [ 0 / 1 ] f[0/1][0/1] 表示在左、右端点这样选择的情况下的最优值,询问直接合并就可以了。
其实这个做法也可以扩展到树上,倍增,同样令 f [ x ] [ i ] [ 0 / 1 ] [ 0 / 1 ] f[x][i][0/1][0/1] 表示以 x x 的第 2 i 1 2^i-1 个父亲为根的子树, x x x x 的第 2 i 1 2^i-1 个父亲的选择情况,也是可以方便地直接合并的,询问时就把最多两条链合并到一起即可,复杂度 O ( n l o g n ) O(nlogn)
代码写得比较丑……应该有不少分类讨论是没有必要的……

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
const int Maxn=100010;
const LL inf=10000000000LL;
int n,m,A,X,B,Y,dep[Maxn],fa[Maxn][17];
LL f[Maxn][2],p[Maxn];
struct Edge{int y,next;}e[Maxn<<1];
int last[Maxn],len=0;
void ins(int x,int y)
{
	int t=++len;
	e[t].y=y;e[t].next=last[x];last[x]=t;
}
struct Node{LL F[2][2];int p;}v[Maxn][17];
Node merge(Node a,Node b)
{
	Node c;
	for(int i=0;i<2;i++)
	for(int j=0;j<2;j++)
	{
		LL v1=a.F[i][0]+b.F[1][j]-min(f[a.p][0],f[a.p][1]);
		LL v2=a.F[i][1]+b.F[0][j]-f[a.p][1];
		LL v3=a.F[i][1]+b.F[1][j]-min(f[a.p][0],f[a.p][1]);
		c.F[i][j]=min(min(v1,v2),min(v3,inf));
	}
	c.p=b.p;
	return c;
}
Node get(int A,int B)
{
	Node re;
	bool fir=true;
	for(int i=16;i>=0;i--)
	if((1<<i)<=dep[A]-dep[B]+1)
	{
		if(fir)fir=false,re=v[A][i];
		else re=merge(re,v[A][i]);
		A=fa[A][i];
	}
	return re;
}
char str[5];
void dfs(int x,int ff)
{
	fa[x][0]=ff;dep[x]=dep[ff]+1;f[x][0]=0;f[x][1]=p[x];
	for(int i=1;(1<<i)<=dep[x];i++)fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int i=last[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==ff)continue;
		dfs(y,x);
		f[x][0]+=f[y][1];
		f[x][1]+=min(f[y][0],f[y][1]);
	}
	v[x][0].F[0][0]=f[x][0],v[x][0].F[1][1]=f[x][1];
	v[x][0].F[0][1]=v[x][0].F[1][0]=inf;
	v[x][0].p=x;
}
void DFS(int x,int ff)
{
	for(int i=1;(1<<i)<=dep[x]+1;i++)v[x][i]=merge(v[x][i-1],v[fa[x][i-1]][i-1]);
	for(int i=last[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==ff)continue;
		DFS(y,x);
	}
}
int LCA(int x,int y)
{
	if(dep[x]<dep[y])swap(x,y);
	for(int i=16;i>=0;i--)
	if((1<<i)<=dep[x]-dep[y])x=fa[x][i];
	if(x==y)return x;
	for(int i=16;i>=0;i--)
	if((1<<i)<=dep[x]&&fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
int jump(int x,int y)
{
	for(int i=16;i>=0;i--)
	if((1<<i)<=dep[x]&&dep[fa[x][i]]>dep[y])x=fa[x][i];
	return x;
}
int main()
{
	scanf("%d%d%s",&n,&m,str);
	for(int i=1;i<=n;i++)scanf("%lld",&p[i]);
	for(int i=1;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		ins(x,y),ins(y,x);
	}
	dep[0]=-1;dfs(1,0);DFS(1,0);
	while(m--)
	{
		scanf("%d%d%d%d",&A,&X,&B,&Y);
		if(dep[A]>dep[B])swap(A,B),swap(X,Y);
		if(fa[B][0]==A&&!X&&!Y){puts("-1");continue;}
		if(A==1)
		{	
			int t=jump(B,1);
			Node tmp=get(B,t);
			if(!X)printf("%lld\n",f[1][0]-f[t][1]+tmp.F[Y][1]);
			else printf("%lld\n",f[1][1]-min(f[t][0],f[t][1])+min(tmp.F[Y][0],tmp.F[Y][1]));
			continue;
		}
		int C=LCA(A,B);
		if(A==C)
		{
			int t1=jump(B,A),t2=jump(A,1);
			Node tmp1=get(B,t1),tmp2=get(A,t2),tmp;
			tmp2.F[X^1][0]=tmp2.F[X^1][1]=inf;
			tmp=merge(tmp1,tmp2);
			LL ans0=f[1][0]-f[t2][1]+tmp.F[Y][1];
			LL ans1=f[1][1]-min(f[t2][0],f[t2][1])+min(tmp.F[Y][0],tmp.F[Y][1]);
			printf("%lld\n",min(ans0,ans1));
		}
		else
		{
			int t1=jump(B,C),t2;
			Node tmp=get(A,C),tmp1=get(B,t1),tmp2;
			tmp.F[X^1][0]=tmp.F[X^1][1]=inf;
			tmp1.F[Y^1][0]=tmp1.F[Y^1][1]=inf;
			tmp.F[X][0]=tmp.F[X][0]-f[t1][1]+tmp1.F[Y][1];
			tmp.F[X][1]=tmp.F[X][1]-min(f[t1][0],f[t1][1])+min(tmp1.F[Y][0],tmp1.F[Y][1]);
			if(C==1)printf("%lld\n",min(tmp.F[X][0],tmp.F[X][1]));
			else if(fa[C][0]==1)
			{
				LL ans0=f[1][0]-f[C][1]+tmp.F[X][1];
				LL ans1=f[1][1]-min(f[C][0],f[C][1])+min(tmp.F[X][0],tmp.F[X][1]);
				printf("%lld\n",min(ans0,ans1));
			}
			else
			{
				t2=jump(C,1);
				tmp2=get(fa[C][0],t2);
				tmp=merge(tmp,tmp2);
				LL ans0=f[1][0]-f[t2][1]+tmp.F[X][1];
				LL ans1=f[1][1]-min(f[t2][0],f[t2][1])+min(tmp.F[X][0],tmp.F[X][1]);
				printf("%lld\n",min(ans0,ans1));
			}
		}
	}
}

猜你喜欢

转载自blog.csdn.net/baidu_36797646/article/details/84197604