1. 程式人生 > >NOIP2018 Day2T3 保衛王國 倍增

NOIP2018 Day2T3 保衛王國 倍增

題目描述:

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
            
           

相關推薦

NOIP2018 Day2T3 保衛王國 倍增

題目描述: Z 國有n座城市,n - 1條雙向道路,每條雙向道路連線兩座城市,且任意兩座城市 都能通過若干條道路相互到達。 Z 國的國防部長小 Z 要在城市中駐紮軍隊。駐紮軍隊需要滿足如下幾個條件: 一座城市可以駐紮一支軍隊,也可以不駐紮軍隊。 由道路直接連線的兩座城市中至少要有

[BZOJ5466][NOIP2018]保衛王國 倍增

題面 首先可以寫一個暴力dp的式子,非常經典的樹形dp \(dp[i][0]\)表示\(i\)這個點沒有駐軍,\(dp[i][1]\)就是有駐軍,\(j\)是\(i\)的孩子。那麼顯然: \[ \begin{align*} dp[i][0]&=dp[j][1]\\ dp[i][1]&=\m

動態DP--NOIP2018 D2T3保衛王國

其實真實做法是倍增 ,但為了練習就用 d d p

noip2018 d2t3 保衛王國 解題報告

保衛王國 電腦卡懶得把題面挪過來了。 樸素 \[ dp_{i,0}=\sum dp_{s,1}\\ dp_{i,1}=\sum \min(dp_{s,0},dp_{s,1})+p_i \] 然後直接動態dp就行了 我發現lct是最好寫的,反正比樹剖好寫,還比她快 沒倍增快,但是看起來倍增挺難寫的.

BZOJ5466 NOIP2018保衛王國倍增+樹形dp)

  暴力dp非常顯然,設f[i][0/1]表示i號點不選/選時i子樹內的答案,則f[i][0]=Σf[son][1],f[i][1]=a[i]+Σmin(f[son][0],f[son][1])。   注意到B的部分分,可以想到每次修改只會對修改點到根的路徑上的點的dp值產生影響。   考慮如何優化修改路

[jzoj]5966. 【NOIP2018提高組D2T3】保衛王國(矩陣乘法+鏈剖維護線段樹 或 倍增DP)

Problem 弱化版動態詢問一棵樹的最小覆蓋集. 每次只選擇其中某兩個點必選或必不選,且詢問獨立. Data constraint n

luogu5024 [NOIp2018]保衛王國 (動態dp)

可以直接套動態dp,但因為它詢問之間相互獨立,所以可以直接倍增記x轉移到fa[x]的矩陣 1 #include<bits/stdc++.h> 2 #define CLR(a,x) memset(a,x,sizeof(a)) 3 using namespace std;

NOIP2018 Day2 T3 保衛王國 - 動態dp - 樹剖 - 線段樹

直接裸上動態dp即可,因為某些不知道的原因NOIP能過,別的地方不開O2都過不了。 #include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) #define lint long long #defi

競賽題解 - NOIP2018 保衛王國

\(\mathcal{NOIP2018}\) 保衛王國 - 競賽題解 按某一個炒雞dalao名曰 taotao 的話說: \(\ \ \ \ \ \ \ \ \ “一道sb倍增題”\) 順便提一下他的〔題解〕(因為按照這個思路寫的,所以程式碼看起來也差不多) 因為比較復(胡)雜(炸),可能需要理解久一

jzoj5966. NOIP2018D2T3 保衛王國(動態dp,倍增

題面 Z國有n座城市,n-1條雙向道路,每條雙向道路連線兩座城市,且任意兩座城市都能通過若干條道路相互到達。 Z國的國防部長小Z要在城市中駐紮軍隊。駐紮軍隊需要滿足如下幾個條件: ①一座城市可以駐紮一支軍隊,也可以不駐紮軍隊。 ②由道路直接連線的兩座城市中至少要有一座城市駐紮軍隊。

luogu P5024 NOIP2018 保衛王國 動態dp

題意 給你一個樹,每次強制選點或者強制不選,詢問最小權覆蓋 這個東西就是個動態 d p

NOIP2018提高組D2T3】保衛王國

抱歉,本人蒟蒻沒能做出,不過可給標程,orz大佬 const maxn=100000;inf=trunc(1e+12)-1; var f : array[0..maxn,0..17,0..1,0..1]of int64; up,dw : array[0..maxn,0..

@NOIP2018 - [email protected] 保衛王國

目錄 @題目描述@ @題解@ @程式碼@ 【部落格搬運 * 6】 @題目描述@ Z 國有n座城市,n−1 條雙向道路,每條雙向道路連線兩座城市,且任意兩座城市 都能通過若干條道路相互到達。 Z 國的國防部長小 Z 要在城市中駐紮軍隊。駐紮軍隊需要滿足如下幾個條件: (1)一座

JZOJ5966【NOIP2018提高組D2T3】保衛王國(並查集)

題目 還是懶得把題目放上來了。 大意:給你一棵帶點權的樹,你要花費一些代價選擇一些點使得相鄰的兩個點至少有一個被選。 然後有很多個詢問,每個詢問強制兩個點的狀態,問強制了這兩個點的狀態後的方案。 比賽思路 沒時間了,沒時間了…… 匆匆打個44分的暴力就好了。 結果混淆了

【比賽】NOIP2018 保衛王國

DDP模板題 #include<bits/stdc++.h> #define ui unsigned int #define ll long long #define db double #define ld long double #define ull unsigned long l

5966. 【NOIP2018提高組D2T3】保衛王國

Description Z國有n座城市,n-1條雙向道路,每條雙向道路連線兩座城市,且任意兩座城市都能通過若干條道路相互到達。 Z國的國防部長小Z要在城市中駐紮軍隊。駐紮軍隊需要滿足如下幾個條件: ①一座城市可以駐紮一支軍隊,也可以不駐紮軍隊。 ②由道路直接連線的兩座城市中至少要有一座城

NOIP2018保衛王國

++ oid image int 答案 pre () getchar min 題目大意:給一顆有點權的樹,每次規定兩個點選還是不選,求這棵樹的最小權點覆蓋。 題解 ZZ碼農題。 要用動態dp做,這題就是板子,然鵝並不會,留坑代填。 因為沒有修改,所以可以靜態倍增。 我們先做

[luogu 5024] 保衛王國

luogu 5024(保衛王國) Problem Here Solution 這大概是一篇重複累贅的blog吧。 最小覆蓋集=全集-最大獨立集 強制取或不取,可以通過將權值修改成inf或者-inf 然後就用動態dp的套路就行了 #include<bits/s

[Luogu P5024] [BZOJ 5466] [NOIp 2018tg]保衛王國

洛谷傳送門 BZOJ傳送門 題目描述 Z Z Z 國有

【NOIP 2018】保衛王國(動態dp)

題目連結  這個$dark$題,嗯,不想說了。雖然早有聽聞動態$dp$,但到最近才學,如果你瞭解動態$dp$,那就能很輕鬆做出這道題了。故利用這題在這裡科普一下動態$dp$的具體內容。 首先大家肯定都會這道題的$O(n^2)$的做法,這是一個很經典的樹形$dp$。具體來講就是一下兩個轉移: $