P3554 [POI2013]LUK-Triumphal arch 题解
P3554 [POI2013]LUK-Triumphal arch 题解
间隙
知识点
-
树形\(DP\) , 二分答案
分析
题意翻译里已经写的很清楚了,这里就不重复一遍了
首先这里的答案\(k\)具有可二分性,考虑二分答案
当\(B\)走到一个节点\(u\)时,第一步要做的肯定是把\(u\)的儿子全部都涂色
如果一个节点的儿子数大于当前的\(k\),则无论怎么涂都会输
反之,说明除了涂自己的儿子外还可以"提前"涂其他的节点
设\(f[i]\)为以\(i\)根节点的子树需要其祖先"提前"染色多少个点才能覆盖整个子树
易得状态转移方程:\(f[u] = max(0 , \sum f[v]+1-k)\)
具体解释\(:\)
代码实现
直接二分答案\(k\),对于每一个\(k\)进行\(dp\)
如果\(f[1]\)为\(0\),则说明可行
反之说明不可行
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5+10;
int n;
struct e{
int to,next;
}edge[MAXN<<1];
int head[MAXN<<1],cnt = 0;
int f[MAXN];
void add(int u,int v){//前向星
edge[++cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt;
}
void dfs(int u,int fa,int k){
int sum = 0;
for(int i=head[u];i;i=edge[i].next){
int v = edge[i].to;
if(v==fa) continue;
dfs(v,u,k);
sum=sum+f[v]+1;//记录
}
f[u] = max(0,sum-k);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n-1;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
int l = 0,r = 10e10;
int ans = -1;
while(l<=r){//二分答案
memset(f,0,sizeof(f));
int mid = (l+r)>>1;
dfs(1,0,mid);//dp
if(f[1]==0){//如果f[1]为0 说明可行
ans = mid;
r = mid-1;
}反之,不可行
else l = mid+1;
}
printf("%d",ans);
}