NOIP2018 Day2T3 保衛王國 倍增
題目描述:
Z 國有n座城市,n - 1條雙向道路,每條雙向道路連線兩座城市,且任意兩座城市 都能通過若干條道路相互到達。
Z 國的國防部長小 Z 要在城市中駐紮軍隊。駐紮軍隊需要滿足如下幾個條件:
一座城市可以駐紮一支軍隊,也可以不駐紮軍隊。
由道路直接連線的兩座城市中至少要有一座城市駐紮軍隊。
在城市裡駐紮軍隊會產生花費,在編號為i的城市中駐紮軍隊的花費是p_i。
小 Z 很快就規劃出了一種駐紮軍隊的方案,使總花費最小。但是國王又給小 Z 提出 了m個要求,每個要求規定了其中兩座城市是否駐紮軍隊。小 Z 需要針對每個要求逐一 給出回答。具體而言,如果國王提出的第j個要求能夠滿足上述駐紮條件(不需要考慮第j個要求之外的其它要求),則需要給出在此要求前提下駐紮軍隊的最小開銷。如果國王提出的第j個要求無法滿足,則需要輸出-1 (1 ≤ j ≤ m)。現在請你來幫助小 Z。
題解:
我們先來考慮序列(也就是一條鏈)應該怎麼做。顯然兩個特殊點把整個序列分為了三部分,而區間之間的資訊也是很好合並的,很容易想到用線段樹或者倍增做,對於每個區間記錄
表示在左、右端點這樣選擇的情況下的最優值,詢問直接合並就可以了。
其實這個做法也可以擴充套件到樹上,倍增,同樣令
表示以
的第
個父親為根的子樹,
和
的第
個父親的選擇情況,也是可以方便地直接合並的,詢問時就把最多兩條鏈合併到一起即可,複雜度
。
程式碼寫得比較醜……應該有不少分類討論是沒有必要的……
程式碼:
#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$。具體來講就是一下兩個轉移:
$