题解:P12017 [NOISG 2025 Finals] 可达性
P12017 [NOISG 2025 Finals] Reachability
Algorithm:
树形背包。
Solution:
被薄纱了。
看到这道题让我会想到了省选的岁月,但其实两道题目并没有任何相似之处。
观察到
- 当
l_u=l_v 时,u,v 之间的边要么为双向边或者没有连边,原因是若为单向边则两个点能够到达点的数量至少差1 。 - 当
l_u \neq l_v 是,u,v 之间的便要么为单向边(l 值大的点连向l 值小的点)或者没有连边。
加上之前的观察,不难发现是一个树上背包的形式。所以考虑设
- 当
l_u=l_v 时: -
- 当
l_u > l_v 时: -
- 当
l_u<l_v 时: -
不满足的情况可以直接输出 NO
退出。最后检查根节点是否合法即可。在实际实现的过程中会遇到
Code:
namespace Mr_Az{
const int N=5008;
int T=1;
int n;
int l[N],siz[N];
bool f[N][2*N],g[2*N];
vector<int> e[N];
void dfs(int u,int fa){
siz[u]=1;
for(auto v:e[u]){
if(v==fa) continue;
dfs(v,u);
if(l[u]==l[v]){// 所达城市数量一致
// 1. 连边
for(rint i=0;i<=siz[u];i++)
for(rint j=0;j<=siz[v];j++)
g[i+j]|=(f[u][i]&f[v][j]);
// 2. 断开(下面必须要满足要求)
if(f[v][l[v]]){
for(rint i=0;i<=siz[u];i++) g[i]|=f[u][i];
}
}
else{// 所达城市数量不一致
if(l[u]>l[v]){// 1. u->v 或不连边
if(!f[v][l[v]]){puts("NO");exit(0);}
else{
for(rint i=0;i<=siz[u];i++){
g[i+l[v]]|=f[u][i];
g[i]|=f[u][i];
}
}
}
else{// 2. v->u 或不连边
if(!f[v][l[v]]&&!f[v][l[v]-l[u]]){puts("NO");exit(0);}
for(rint i=0;i<=siz[u];i++) g[i]|=f[u][i];
}
}
siz[u]+=siz[v];
for(rint i=0;i<=siz[u];i++) f[u][i]=g[i];
mem(g,0);
}
}
inline void solve(){
read(n);
for(rint i=1;i<=n;i++) read(l[i]);
for(rint i=1,u,v;i<n;i++){
read(u,v);
e[u].pb(v);e[v].pb(u);
}
for(rint i=1;i<=n;i++) f[i][1]=1;
dfs(1,0);
if(f[1][l[1]]) puts("YES");
else puts("NO");
}
inline void mian(){if(!T) read(T);while(T--) solve();}
}