P7929 [COCI2021-2022#1] Logičari

P7929 [COCI2021-2022#1] Logičari

P7929 [COCI2021-2022#1\] Logičari - 洛谷 \| 计算机科学教育新生态 (luogu.com.cn)](https://www.luogu.com.cn/problem/P7929) #### 文章目录 * [P7929 \[COCI2021-2022#1\] Logičari](#1] Logičari) * * [题目大意](#题目大意) * [思路](#思路) * [code](#code) ### 题目大意 给定一棵 n n n 个节点的基环树,现在对树上的节点染色,使得每个电都有且仅有一个与他相连的点被染色(不包括自身),求最少染色次数,无解输出 − 1 -1 −1 3 ≤ n ≤ 1 0 5 3\\le n \\le 10\^5 3≤n≤105 ### 思路 我们考虑一棵普通的树看看怎么解决。 很明显是一个树形 d p dp dp 乱搞就好了。 我们先 d f s dfs dfs 找到多出来的那条返祖边,设两个端点分别为 S , T S , T S,T 然后我们考虑不合法的情况: 1、 S S S 染了,但 T T T 旁边又染了一个。 2、 T T T 染了,但 T T T 旁边又染了一个。 3、 S S S 没染, T T T 旁边的那个也没染,而且 T T T 没有其他子树了。 在遇到 T T T 的时候特判一下就好了。 我们考虑转移。 设 d p \[ x \] \[ a \] \[ b \] dp\[x\]\[a\]\[b\] dp\[x\]\[a\]\[b\] 表示现在处理到以 x x x 为根节点的子树, x x x 的状态为 a a a , x x x 父亲节点的状态为 b b b 的最小代价。 设 w \[ x \] \[ a \] w\[x\]\[a\] w\[x\]\[a\] 表示以 x x x 为根的子树中, x x x 的状态为 a a a 的最小代价。 设 v a l = ∑ y = s o n \[ x \] w \[ y \] \[ 0 \] val = \\sum_{y = son\[x\]}w\[y\]\[0\] val=∑y=son\[x\]w\[y\]\[0

如果 f a [ u ] fa[u] fa[u] 被染了,那么 x x x 的儿子都不能染,所以答案为
d p [ x ] [ a ] [ b ] = v a l + a dp[x][a][b] = val + a dp[x][a][b]=val+a

否则
d p [ x ] [ a ] [ b ] = min ⁡ y = s o n [ x ] ( v a l − w [ y ] [ 0 ] + w [ y ] [ 1 ] ) + a dp[x][a][b] = \min_{y = son[x]}(val - w[y][0] + w[y] [1]) + a dp[x][a][b]=y=son[x]min(val−w[y][0]+w[y][1])+a

code

cpp 复制代码
#include <bits/stdc++.h>
#define LL long long
#define fu(x , y , z) for(x = y ; x <= z ; x ++)
using namespace std;
const int inf = 1e9 + 5 , N = 1e5 + 5;
int hd[N] , cnt , s , t , vis[N] , flg , fs , ft , out , n;
LL f[N][2][2] , w[N][2];
struct E {
    int to , nt;
} e[N << 1];
void add (int x , int y) { e[++cnt].to = y , e[cnt].nt = hd[x] , hd[x] = cnt; }
void dfs (int x , int fa) {
    int y;
    vis[x] = 1;
    for (int i = hd[x] ; i ; i = e[i].nt) {
        y = e[i].to;
        if (y == fa) continue; 
        if (vis[y]) flg = i;
        else dfs (y , x);
    }
}
LL dp (int x , int fa , int a , int b) {
    if (f[x][a][b]) return f[x][a][b];
    int y;
    LL sum = 0 , ans = inf;
    for (int i = hd[x] ; i ; i = e[i].nt) {
        y = e[i].to;
        if ((y == fa) || (i == flg) || ((i ^ 1) == flg)) continue;
        if (y == t) {
            if ((a && fs) || (b && ft) || (!a && !fs && out < 3)) return inf;
            w[y][ft] = dp (y , x , ft , (fs | a));
            w[y][!ft] = inf;
            sum += w[y][0];
        }
        else {
            w[y][0] = dp (y , x , 0 , a);
            sum += w[y][0];
            if (!b) w[y][1] = dp (y , x , 1 , a);
        } 
    }
    if (b) return f[x][a][b] = sum + a;
    for (int i = hd[x] ; i ; i = e[i].nt) {
        y = e[i].to;
        if ((y == fa) || (i == flg) || ((i ^ 1) == flg)) continue;
        ans = min (ans , sum - w[y][0] + w[y][1]);
    }
    return f[x][a][b] = ans + a;
}
int main () {
    int x , y;
    LL ans;
    cnt = 1;
    scanf ("%d" , &n);
    for (int i = 1 ; i <= n ; i ++) {
        scanf ("%d%d" , &x , &y);
        add (x , y) , add (y , x);
    }
    dfs (1 , 0);
    s = e[flg].to , t = e[flg ^ 1].to;
    for (int i = hd[t] ; i ; i = e[i].nt) ++out;
    // cout << out << " " << t;
    // return 0;
    fs = ft = 0;
    ans = dp (s , 0 , 0 ,0);
    memset (f , 0 , sizeof (f));
    fs = 1 , ft = 0;
    ans = min (ans , dp (s , 0 , 1 , 0));
    memset (f , 0 , sizeof (f));
    fs = 0 , ft = 1;
    ans = min (ans , dp (s , 0 , 0 , 1));
    memset (f , 0 , sizeof (f));
    fs = ft = 1;
    ans = min (ans , dp (s , 0 , 1 , 1));
    if (ans <= 1ll * n) printf ("%lld" , ans);
    else printf ("-1");
    return 0;
}
相关推荐
Coovally AI模型快速验证1 分钟前
检测+跟踪一体化!4.39M参数、8.3W功耗,轻量化模型让无人机在露天矿实时巡检
算法·yolo·无人机·智能巡检·智慧矿山
玛卡巴卡ldf4 分钟前
【LeetCode 手撕算法】(矩阵)73-矩阵置零、54-螺旋矩阵(贪吃蛇)、48-旋转图像
java·数据结构·算法·leetcode·力扣
C^h4 分钟前
RTthread中的内存池理解
linux·数据库·c++·算法·嵌入式
深藏功yu名5 分钟前
Day25(高阶篇):RAG检索与重排序算法精研|从原理到参数调优,彻底攻克检索瓶颈
人工智能·算法·ai·自然语言处理·排序算法·agent
郝学胜-神的一滴9 分钟前
深入解析:生成器在UserList中的应用与Python可迭代对象实现原理
开发语言·python·程序人生·算法
雪木木9 分钟前
刷题:力扣热题100--滑动窗口(Day03)
算法·leetcode
Yzzz-F12 分钟前
Problem - 2157D - Codeforces
算法
颜酱16 分钟前
回溯算法实战练习(2)
javascript·后端·算法
We་ct21 分钟前
LeetCode 153. 旋转排序数组找最小值:二分最优思路
前端·算法·leetcode·typescript·二分·数组
享哥27 分钟前
tick 数据探索笔记:从抓取到理解
算法