JZOJ5966. 【NOIP2018提高组D2T3】保卫王国

Description

Z国有n座城市,n-1条双向道路,每条双向道路连接两座城市,且任意两座城市都能通过若干条道路相互到达。
Z国的国防部长小Z要在城市中驻扎军队。驻扎军队需要满足如下几个条件:
①一座城市可以驻扎一支军队,也可以不驻扎军队。
②由道路直接连接的两座城市中至少要有一座城市驻扎军队。
③在城市里驻扎军队会产生花费,在编号为i的城市中驻扎军队的花费是pi。
小Z很快就规划出了一种驻扎军队的方案,使总花费最小。但是国王又给小Z提出了m个要求,每个要求规定了其中两座城市是否驻扎军队。小Z需要针对每个要求逐一给出回答。具体而言,如果国王提出的第j个要求能够满足上述驻扎条件(不需要考虑第j个要求之外的其它要求),则需要给出在此要求前提下驻扎军队的最小开销。如果国王提出的第j个要求无法满足,则需要输出-1(1<=j<=m) 。现在请你来帮助小Z。

Input

第1行包含两个正整数n,m 和一个字符串type ,分别表示城市数、要求数和数据类型。type 是一个由大写字母A,B或C和一个数字1,2,3组成的字符串。它可以帮助你获得部分分。你可能不需要用到这个参数。这个参数的含义在【数据规模与约定】中有具体的描述。
第2 行n个整数pi ,表示编号i的城市中驻扎军队的花费。
接下来n-1行,每行两个正整数u,v ,表示有一条u 到v 的双向道路。
接下来 m行,第 j行四个整数a,x,b,y(a<>b) ,表示第j个要求是在城市a驻扎x支军队,在城市b驻扎y支军队。其中,x、y的取值只有0或1:若x为0,表示城市a不得驻扎军队,若x为1,表示城市a必须驻扎军队;若y为0,表示城市b不得驻扎军队,若y为1,表示城市b必须驻扎军队。
输入文件中每一行相邻的两个数据之间均用一个空格分隔。

Output

输出共 行,每行包含1个整数,第j行表示在满足国王第j个要求时的最小开销,如果无法满足国王的第j个要求,则该行输出-1。

SOLUTION

  • “驻扎军队”,危

题意:给出一棵树,相邻两点之间必须有一个点驻扎军队每次询问给定两个点驻扎的状态,问满足此要求的驻扎方案的最小花费。

先考虑没有约束的时候一棵树的答案,一个小学生DP就可以解决这个问题。

设f[x][0/1]表示x这个点选或不选的子树的最小值。
对于一个点,如果它的下面有被限制的点,那么只有这个点向上来的这条路径会受到影响,其他的转移的f值是不受到影响的,我们可以只将限制的点之间的路径重新转移一遍,别的子树直接套用预处理好的f值,那么就可以用路径长度的时间去查询。

我们不难发现由于所有的计算是求和的,所以我们很容易计算不包括一个儿子的答案。

最后我们就用倍增来加速这个枚举两点之间路径的过程。
考虑每一个点,设w[x][t][0/1][0/1]表示向上2t步,x的状态(选1或不选0),以及fa[x][t]的状态下,沿x至fa[x][t]的路上的DP值使得满足这个状态的最小和。

注意一些细节即可。

#include
#include
#include
#include
#define maxn 100005
#define maxm 200010
#define ll long long 
#define maxp 18
#define min(a,b) (a>b)?b:a
using namespace std;

int n,m,t,i,j,k,l,x,y,a,b,lca,fa[maxn][maxp+1],dep[maxn];
int em,e[maxm],ls[maxn],nx[maxm];
ll f[maxn][2],g[maxn][2],p[maxn],w[maxn][maxp+1][2][2],v[maxn][2],M1[2],M2[2],M[2],ans;
char ch;

void insert(int x,int y){
	em++; e[em]=y; nx[em]=ls[x]; ls[x]=em;
	em++; e[em]=x; nx[em]=ls[y]; ls[y]=em;
}

void dp(int x,int pre){
	dep[x]=dep[pre]+1;
	fa[x][0]=pre; 
	f[x][0]=0,f[x][1]=p[x];
	for(int i=1;i<=maxp;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int i=ls[x];i;i=nx[i]) if (e[i]!=pre){
		dp(e[i],x);
		f[x][0]+=g[e[i]][0];
		f[x][1]+=g[e[i]][1];
	}
	g[x][0]=f[x][1];
	g[x][1]=min(f[x][0],f[x][1]);
}

void hb(int x,int t){
	for(i=0;i<2;i++) for(j=0;j<2;j++) w[x][t+1][i][j]=1e10;
	int y=fa[x][t];
	for(i=0;i<2;i++) for(j=0;j<2;j++) for(k=0;k<2;k++) 
			w[x][t+1][i][k]=min(w[x][t+1][i][k],w[x][t][i][j]+w[y][t][j][k]);
}

void dfs(int x,int pre){
	if (pre){
		v[x][0]=v[pre][1]+f[pre][1]-g[x][1];
		v[x][1]=min(v[x][0],v[pre][0]+f[pre][0]-g[x][0]);
		w[x][0][0][0]=1e10;
		w[x][0][1][0]=f[pre][0]-g[x][0];
		w[x][0][0][1]=f[pre][1]-g[x][1];
		w[x][0][1][1]=f[pre][1]-g[x][1];	
	} 
	for(int i=1;i<=maxp;i++) if (fa[fa[x][i-1]][i-1]) hb(x,i-1); else break;
	for(int i=ls[x];i;i=nx[i]) if (e[i]!=pre) dfs(e[i],x);
}

int getlca(int x,int y){
	if (dep[x]=0;i--) if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
	if (x==y) return x;
	for(int i=maxp;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}

int main(){
	freopen("defense.in","r",stdin);
	freopen("defense.out","w",stdout);
	scanf("%d %d %c%c",&n,&m,&ch,&ch);
	for(i=1;i<=n;i++) scanf("%d",&p[i]);
	for(i=1;i=0;t--) if (dep[fa[x][t]]>dep[lca]){
				memcpy(M,M1,sizeof(M));
				M1[0]=M1[1]=1e10;
				for(i=0;i<2;i++) for(j=0;j<2;j++) 
					M1[j]=min(M1[j],M[i]+w[x][t][i][j]);
				x=fa[x][t];
			}
		} else M1[a^1]=1e10;
		if (y!=lca){
			M2[b]=f[y][b],M2[b^1]=1e10;
			for(t=maxp;t>=0;t--) if (dep[fa[y][t]]>dep[lca]){
				memcpy(M,M2,sizeof(M));
				M2[0]=M2[1]=1e10;
				for(i=0;i<2;i++) for(j=0;j<2;j++)
					M2[j]=min(M2[j],M[i]+w[y][t][i][j]);
				y=fa[y][t];
			}
		} else M2[b^1]=1e10;
		ans=1e10;
		if (y==lca){
			for(i=0;i<2;i++) for(j=0;j<2;j++) if (i|j)
				ans=min(ans,M1[i]+M2[j]+v[lca][j]+f[lca][j]-g[x][j]);	
    		} else for(i=0;i<2;i++) for(j=0;j<2;j++) for(k=0;k<2;k++) if ((i|j)&&(j|k))
    			ans=min(ans,M1[i]+v[lca][j]+M2[k]+f[lca][j]-g[x][j]-g[y][j]);
		if (ans==1e10) printf("-1\n"); else printf("%lld\n",ans);
	}
}

你可能感兴趣的:(题解,树,DP)