[COCI2021-2022#1] Logičari - 题解
SAMSHAWCRAFT · · 题解
基环树,就是将一棵树的两个顶点用一条边 E 连起来形成的图。这个图上有且只有一个环,并且这个环上必定有上述的边 E。
现在我们有一颗基环树,要在上面染色。我们知道如果在一棵树上像题目中说的那样染色,可以用树形 DP 解决。而对于本题我们可以考虑把这个基环树的环切开,切口一端当作树根,另一端当作树根的“兄弟”,之后 DP,再看切口两侧染色的情况是否符合题意。
怎么确定切口?可以借助并查集,当一条边 E 的两个端点 u,v 在并查集中已经处于一个集合中,换言之是 u,v 已经在一棵树上,那么连接 u,v 的这条边 E 就是基环树上的环上的一条边,此时可以选 u 做树根(在代码中对应变量
分类讨论:
树形 DP 的过程比较基础,设计状态
无解的情况只有三种,特判一下:
细节较多,代码如下,仅供参考。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <numeric>
#define qaq inline
using ll=long long;
const int inf=1e9+7;
const int sz=1e5+19;
const int esz=2e5+19;
int n,head[sz],hpp=0,root,rootbro,ans;
struct edge{
int nxt,to;
}graph[esz];
qaq void addEdge(int from,int to){
graph[++hpp]=edge{head[from],to};
head[from]=hpp;
}
struct UnionFindSet{
int fa[sz];
qaq void clear(int n=sz-1){
for(int cx=0;cx<=n;++cx)
fa[cx]=cx;
}
int findAnc(int id){
if(fa[id]==id) return id;
return fa[id]=findAnc(fa[id]);
}
qaq bool connect2(int u,int v){
return findAnc(u)==findAnc(v);
}
qaq void join(int u,int v){
int fu=findAnc(u);
int fv=findAnc(v);
if(fu==fv) return;
fa[fu]=fv;
}
}UFS;
int pr[sz];
void DFS(int u,int fau){
pr[u]=fau;
for(int p=head[u];p!=0;p=graph[p].nxt){
int v=graph[p].to;
if(v==fau) continue;
DFS(v,u);
}
}
int dp[sz][2][2][2][2];
int color(int u,int th,int fac,int rtc,int rtbroc){
///@param u: currently visited vertex
///@param th: equals to 1 only when u is colored
///@param fac: equals to 1 only when u's father is colored
///@param rtc: equals to 1 only when root is colored
///@param rtbroc: equals to 1 only when rootbro is colored
if(dp[u][th][fac][rtc][rtbroc]!=-1)
return dp[u][th][fac][rtc][rtbroc];
ll res=inf;
bool valid=true;
if(u==root&&th!=rtc) valid=false;
if(u==rootbro&&th!=rtbroc) valid=false;
if(u==rootbro&&fac&&rtc) valid=false;
if(!valid) return dp[u][th][fac][rtc][rtbroc]=inf;
bool cfree=false;
///@param cfree: true only when u is not able to be colored
if(fac) cfree=true;
if(u==root&&rtbroc) cfree=true;
if(u==rootbro&&rtc) cfree=true;
ll ccnt=th;
///@param ccnt: the number of currently colored vertexes
for(int p=head[u];p!=0;p=graph[p].nxt){
int v=graph[p].to;
if(v==pr[u]) continue;
ccnt+=color(v,0,th,rtc,rtbroc);
}
if(cfree){
res=std::min(res,ccnt);
}else{
for(int p=head[u];p!=0;p=graph[p].nxt){
int v=graph[p].to;
if(v==pr[u]) continue;
res=std::min(res,ccnt-color(v,0,th,rtc,rtbroc)+color(v,1,th,rtc,rtbroc));
}
}
return dp[u][th][fac][rtc][rtbroc]=res;
}
int main(){
scanf("%d",&n);
UFS.clear(n);
memset(dp,-1,sizeof dp);
for(int cx=1,u,v;cx<=n;++cx){
scanf("%d%d",&u,&v);
if(UFS.connect2(u,v)){
root=u,rootbro=v;
}else{
UFS.join(u,v);
addEdge(u,v);
addEdge(v,u);
}
}
DFS(root,0);
int ans=inf;
ans=std::min({ans,color(root,0,0,0,0),color(root,0,0,0,1),
color(root,1,0,1,0),color(root,1,0,1,1)});
if(ans==inf) puts("-1");
else printf("%d\n",ans);
return 0;
}