[bzoj 5466]保衛王國

Z國有n座城市,n-1條雙向道路,每條雙向道路連接兩座城市,且任意兩座城市都能通過若干條道路相互到達。
Z國的國防部長小 Z 要在城市中駐紮軍隊。駐紮軍隊需要滿足如下幾個條件:一座城市可以駐紮一支軍隊,也可以不駐紮軍隊。由道路直接連接的兩座城市中至少要有一座城市駐紮軍隊。
在城市裏駐紮軍隊會產生花費,在編號爲i的城市中駐紮軍隊的花費是p_i。小 Z很快就規劃出了一種駐紮軍隊的方案,使總花費最小。但是國王又給小 Z 提出了m個要求,每個要求規定了其中兩座城市是否駐紮軍隊。小Z需要針對每個要求逐一給出回答。具體而言,如果國王提出的第j個要求能夠滿足上述駐紮條件(不需要考慮第 j 個要求之外的其它要求),則需要給出在此要求前提下駐紮軍隊的最小開銷。如果國王提出的第j個要求無法滿足,則需要輸出-1(1≤j≤m)。現在請你來幫助小Z。

這道題ddp做法很顯然,這裏就不介紹了。
這道題我們可以先考慮一個子問題,如果每次只要求一個點,那麼只要換根dp一下就可以了。所以我們發現兩個點的話,先求出兩個點的最近公共祖先p,枚舉狀態爲id,如果能處理出最小的滿足條件的f[p][id](範圍爲p的子樹),還有g[p][id](範圍爲整棵樹-p的子樹),相加就可以了。
g數組是很容易處理的,那關鍵就是怎麼處理f。那因爲只有兩個點,如果我們能處理出一個P[x][y][idx][idy]數組(表示當x的狀態爲idx,y的狀態idy時,範圍爲y的子樹-x的子樹)就可以了,但這樣空間和時間複雜度都過不去,而我們發現這個數組可以轉化成倍增的形式,這樣就可以預處理了。
那查詢的時候,只要在向上跳的時候枚舉中繼點的狀態,就可以計算答案了。總時間複雜度爲O(8nlogn+4qlogn)O(8*nlogn+4*qlogn),算是比較優秀的。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline void write(int x)
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
struct node
{
	int x,y,next;
}a[200010];int len,last[100010];
inline void ins(int x,int y)
{
	len++;
	a[len].x=x;a[len].y=y;
	a[len].next=last[x];last[x]=len;
}
int dep[100010],fa[100010][18];
long long w[100010],f[100010][2],g[100010][2],p[100010][18][2][2];
inline void dfs(int x)
{
	f[x][1]=w[x];
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(y==fa[x][0])continue;
		dep[y]=dep[x]+1,fa[y][0]=x;
		dfs(y);
		f[x][0]+=f[y][1];
		f[x][1]+=min(f[y][0],f[y][1]);
	}
}
inline void dp(int x)
{
	long long t0=g[x][0],t1=g[x][1]+w[x];
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(y==fa[x][0])continue;
		t0+=f[y][1],t1+=min(f[y][0],f[y][1]);
	}
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(y==fa[x][0])continue;
		g[y][0]=t1-min(f[y][0],f[y][1]),g[y][1]=min(t0-f[y][1],g[y][0]);
		dp(y);
	}
}
inline long long myadd(long long w1,long long w2)
{
	long long mn=min(w1,w2);
	if(mn==-1)return mn;
	else return w1+w2;
}
inline long long mymin(long long w1,long long w2)
{
	if(w1>w2)swap(w1,w2);
	if(w1==-1)return w2;
	else return w1;
}
inline void pre(int x)
{
	for(int i=1;(1<<i)<=dep[x];i++)
	{
		int P=fa[x][i-1];
		fa[x][i]=fa[P][i-1];
		for(int d1=0;d1<=1;d1++)
		{
			for(int d2=0;d2<=1;d2++)
			{
				for(int d3=0;d3<=1;d3++)p[x][i][d1][d2]=mymin(p[x][i][d1][d2],myadd(p[x][i-1][d1][d3],p[P][i-1][d3][d2]));
			}
		}			
	}
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(y==fa[x][0])continue;
		p[y][0][1][0]=f[x][0]-f[y][1];
		long long wy=f[x][1]-min(f[y][0],f[y][1]);p[y][0][0][1]=wy,p[y][0][1][1]=wy;
		pre(y);
	}
}
long long nx[2],tx[2],ny[2],ty[2];
inline long long solve(int x,int idx,int y,int idy)
{
	if(dep[x]<dep[y])swap(x,y),swap(idx,idy);
	nx[0]=f[x][0],nx[1]=f[x][1],ny[0]=f[y][0],ny[1]=f[y][1];
	nx[idx^1]=-1,ny[idy^1]=-1;
	for(int i=16;i>=0;i--)
	{
		if(dep[x]-dep[y]>=(1<<i))
		{
			tx[0]=nx[0],tx[1]=nx[1];nx[0]=nx[1]=-1;
			for(int d1=0;d1<=1;d1++)
			{
				for(int d2=0;d2<=1;d2++)nx[d2]=mymin(nx[d2],myadd(tx[d1],p[x][i][d1][d2]));
			}
			x=fa[x][i];
		}
	}
	if(x==y)return myadd(nx[idy],g[y][idy]);
	for(int i=16;i>=0;i--)
	{
		if(dep[x]>=(1<<i) && fa[x][i]!=fa[y][i])
		{			
			tx[0]=nx[0],tx[1]=nx[1];nx[0]=nx[1]=-1;ty[0]=ny[0],ty[1]=ny[1];ny[0]=ny[1]=-1;
			for(int d1=0;d1<=1;d1++)
			{
				for(int d2=0;d2<=1;d2++)nx[d2]=mymin(nx[d2],myadd(tx[d1],p[x][i][d1][d2])),ny[d2]=mymin(ny[d2],myadd(ty[d1],p[y][i][d1][d2]));
			}			
			x=fa[x][i],y=fa[y][i];
		}
	}
	int P=fa[x][0];
	return mymin(myadd(myadd(nx[1],ny[1]),f[P][0]+g[P][0]-f[x][1]-f[y][1]),myadd(myadd(mymin(nx[0],nx[1]),mymin(ny[0],ny[1])),f[P][1]+g[P][1]-min(f[x][0],f[x][1])-min(f[y][0],f[y][1])));
}
char ss[5];
int main()
{
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	int n=read(),m=read();scanf("%s",ss+1);
	for(int i=1;i<=n;i++)w[i]=read();
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read();
		ins(x,y),ins(y,x);
	}
	memset(p,-1,sizeof(p));
	dfs(1),dp(1),pre(1);
	while(m--)
	{
		int x=read(),idx=read(),y=read(),idy=read();
		printf("%lld\n",solve(x,idx,y,idy));
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章