5966. 【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<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#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]<dep[y]) swap(x,y);
	for(int i=maxp;i>=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<n;i++){
		scanf("%d%d",&x,&y);
		insert(x,y);
	} 
	dp(1,0);
	dfs(1,0);
	while (m--){
		scanf("%d%d%d%d",&x,&a,&y,&b);
		lca=getlca(x,y);
		if (x==lca) swap(x,y),swap(a,b);
		memset(M1,0,sizeof(M1));
		memset(M2,0,sizeof(M2));
		if (x!=lca){
			M1[a]=f[x][a],M1[a^1]=1e10;
			for(t=maxp;t>=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);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章