• [COCI2021-2022#1] Logičari


    题目描述

    给定一个 n n n 个点的基环树,现在对基环树上的点染色,使得每个点都有且仅有一个与他相连的点(不包括它自身)被染色,求最少的染色点数,或者返回无解


    n n n 个点, n n n条边的连通无向图是基环树。

    对于基环树上的问题,常把基环树上的环的一条边删去,这样就剩下一棵树,可以做树型 D P DP DP

    要找环上的一条边,可以用并查集。输入时每加一条边就判断连接的两个点 u , v u,v u,v 属于的连通块是否相同,若相同,说明这条边在环上。将 S S S 看作根。

    只需枚举 S , T S,T S,T 的染色状态( 4 4 4 种),对每种情况求最小的答案即可。

    d p u , s , f s , S s , T s dp_{u,s,fs,Ss,Ts} dpu,s,fs,Ss,Ts 表示当前节点 u u u、父亲、 S S S T T T 的染色状态(0/1),所能达到的最少的染色个数(若无解,为 I N F INF INF

    首先先看无解的情况

    • u = S u=S u=S,但是 s ≠ S s s\not=Ss s=Ss u u u 的状态与设定不同)
    • u = T u=T u=T,但是 s ≠ T s s\not=Ts s=Ts(同上)
    • u = T u=T u=T,但是 S s = 1 , f s = 1 Ss=1,fs=1 Ss=1,fs=1 T T T 的旁边有两个 2 2 2 个染色的)

    对于一个点 u u u,下面分类讨论

    • u u u 的子树外,还有与 u u u 连接的点(可能是 f a u , S , T fa_u,S,T fau,S,T)已经被染色。
      说明 u u u 的儿子不能被染色。
      转移方程为 d p u , s , f s , S s , T s = s + ∑ v ∈ s o n u d p v , 0 , s , S s , T s dp_{u,s,fs,Ss,Ts}=s+\sum\limits_{v\in son_u}dp_{v,0,s,Ss,Ts} dpu,s,fs,Ss,Ts=s+vsonudpv,0,s,Ss,Ts
    • 否则说明 u u u 的儿子可以被染色,但只能有一个。
    • 转移方程为 d p u , s , f s , S s , T s = s + min ⁡ v ∈ s o n u ( ( ∑ x ∈ s o n u d p x , 0 , s , S s , T s ) − d p v , 0 , s , S s , T s + d p v , 1 , s , S s , T s ) dp_{u,s,fs,Ss,Ts}=s+\min\limits_{v\in son_u}\left(\left(\sum\limits_{x\in son_u}dp_{x,0,s,Ss,Ts}\right)-dp_{v,0,s,Ss,Ts}+dp_{v,1,s,Ss,Ts}\right) dpu,s,fs,Ss,Ts=s+vsonumin((xsonudpx,0,s,Ss,Ts)dpv,0,s,Ss,Ts+dpv,1,s,Ss,Ts)

    时间复杂度为 O ( n ) O(n) O(n)

    #include
    using namespace std;
    typedef long long ll;
    const ll INF=1e9;
    const int N=1e5+1;
    int n,vis[N],a[21],b[21],S,T,F[N];
    int head[N],nxt[N<<1],to[N<<1],cnt,d[N],fa[N];
    ll dp[N][2][2][2][2];
    void add(int u,int v)
    {
        to[++cnt]=v;
        nxt[cnt]=head[u];
        head[u]=cnt;
    }
    int find(int x)
    {
        return x==F[x]?x:F[x]=find(F[x]);
    }
    bool pd(int x,int y)
    {
        return find(x)==find(y);
    }
    void Add(int x,int y)
    {
        int a=find(x),b=find(y);
        if(a!=b) F[a]=b;
    }
    void dfs(int u,int f)
    {
        if(vis[u]){
            S=u,T=f;
            return;
        }
        for(int i=head[u];i;i=nxt[i]){
            if(to[i]!=f){
                vis[i]=1;
                dfs(to[i],u);
            }
        }
    }
    void Dfs(int u,int f)
    {
        fa[u]=f;
        for(int i=head[u];i;i=nxt[i]) if(to[i]!=f&&(u!=T||to[i]!=S)&&(u!=S||to[i]!=T)) Dfs(to[i],u);
    }
    ll solve(int u,int s,int fs,int Ss,int Ts)
    {
        if(dp[u][s][fs][Ss][Ts]!=-1) return dp[u][s][fs][Ss][Ts];
        ll ans=INF;
        if(u==S&&s!=Ss||u==T&&s!=Ts||u==T&&fs&&Ss) return dp[u][s][fs][Ss][Ts]=INF;
        bool pd=fs||u==S&&Ts||u==T&&Ss;
        ll ccnt=s;
        for(int i=head[u];i;i=nxt[i]){
            if(to[i]!=fa[u]){
                ccnt+=solve(to[i],0,s,Ss,Ts);
            }
        }
        if(pd) ans=min(ans,ccnt);
        else{
            for(int i=head[u];i;i=nxt[i]){
                if(to[i]!=fa[u]){
                    ans=min(ans,ccnt-solve(to[i],0,s,Ss,Ts)+solve(to[i],1,s,Ss,Ts));
                }
            }
        }
        return dp[u][s][fs][Ss][Ts]=ans;
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) F[i]=i;
        for(int i=1,x,y;i<=n;i++){
            scanf("%d%d",&x,&y);
            if(pd(x,y)) S=x,T=y;
            else add(x,y),add(y,x),Add(x,y);
        }
        memset(dp,-1,sizeof(dp));
        Dfs(S,0);
        ll ans=INF;
        ans=min({ans,solve(S,0,0,0,0),solve(S,0,0,0,1),solve(S,1,0,1,0),solve(S,1,0,1,1)});
        if(ans!=INF) printf("%lld\n",ans);
        else puts("-1");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
  • 相关阅读:
    18.EC实战 新建项目工程并配置各个引脚的工作方式(持续更新)
    KY90 简单密码
    MobileViT v2导出onnx模型时遇Col2Im算子无法导出问题
    实现一个博客系统----基于前后端分离
    vue2 减少if else 等判断的出现
    Excel如何设置密码保护【图文详情】
    Oracle数据库从入门到精通系列之十二:段
    对比分析小游戏引擎孰优孰劣
    spring boot Mybatis Plus分页
    lua基础之table
  • 原文地址:https://blog.csdn.net/dygxczn/article/details/133709036