• 练习 3C Tree


    C. Infected Tree

    Problem - C - Codeforces

    问题描述:一个二叉树,根节点被感染了,可以通过删除一个节点使这个树分成两个部分,从而使分离开的子树无法被感染,求这样操作,有最多多少个节点是未被感染的(删除的节点不算未被感染的)。

    思路:树形dp。f[u]表示为u被感染后他的子树有最多多少个的节点未被感染。如果u是叶子节点,那么就是0;如果u只要一个节点,那么就是siz[v] - 1;如果有两个节点,那么就是max(siz[v1] - 1 + f[v2], siz[v2] - 1 + f[v1])
    F ( u ) = { 叶子节点 0 一个子节点 s i z [ v ] − 1 两个子节点 m a x ( s i z [ v 1 ] − 1 + f [ v 2 ] , s i z [ v 2 ] − 1 + f [ v 1 ] ) F(u) = {0siz[v]1max(siz[v1]1+f[v2],siz[v2]1+f[v1]) F(u)= 叶子节点0一个子节点siz[v]1两个子节点max(siz[v1]1+f[v2],siz[v2]1+f[v1])
    边界:无。

    目标:
    F ( 1 ) F(1) F(1)

    注意事项:u的子节点不是g[u].size(),由于建树是双向边,因此多一个这条边。而且,也不能单纯直接令v1 = g[u][0], v2 = g[u][1],原因同上。但是可以用两个变量去处理v1 v2

    代码:

    void solve() {
        int n; cin>>n;
        vector<vector<int>> g(n + 1);
        for(int i = 1; i < n; ++i) {
            int u,v; cin>>u>>v;
            g[u].push_back(v);
            g[v].push_back(u);
        }
        vector<int> f(n + 1), siz(n + 1);
        auto dfs = [&](auto &&dfs, int u, int fu) -> void {
            siz[u] = 1;
            int cnt = 0;
            PII son = {-1,-1};
            for(auto y: g[u]) {
                if(y == fu) continue;
                dfs(dfs, y,u);
                cnt++;
                if(son.vf == -1) son.vf = y;
                else son.vs = y;
                siz[u] += siz[y];
            }
            if(cnt == 0) {
                f[u] = 0;
            }
            if(cnt == 1) {
                f[u] = siz[ son.vf] - 1;
            }
            if(cnt == 2) {
                int v1 = son.vf, v2 = son.vs;
                f[u] = max(siz[v1] - 1 + f[v2], siz[v2] - 1 + f[v1]);
            }
        };
        dfs(dfs,1,-1);
        cout<<f[1]<<endl;
    }
    
    • 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

    C. Kefa and Park

    Problem - C - Codeforces

    问题描述:一个树,叶子节点有餐馆,从根节点出发到餐馆求有多少条路。限制条件是在一条路中,不能走连续k个点权为1的点。

    思路:记录一条路径上连续的1的个数,如果连续的1的个数大于k,则后面怎么都到达不了,否则就有可能。之后叶子节点能否到达即可。

    代码:

    void solve() {
        int n,k; cin>>n>>k;
        vector<int> vw(n + 1),lnow(n + 1), leaf(n + 1);
        vector<vector<int>> g(n + 1);
        for(int i = 1; i <= n; ++i) cin>>vw[i];
        for(int i = 1; i < n; ++i) {
            int u,v; cin>>u>>v;
            g[u].push_back(v);
            g[v].push_back(u);
        }
        auto dfs = [&] (auto &&dfs, int u, int fu) -> void {
            for(auto y: g[u]) {
                if(y == fu) continue;
                if(vw[y] == 0) {
                    lnow[y] = 0;
                } else {
                    lnow[y] = lnow[u] + 1;
                }
                if(lnow[y] > k) continue;
                dfs(dfs, y,u);
            }
            if(g[u].size() == 1) leaf[u] = 1;
        };
        // 对根节点连续1进行处理
        lnow[1] = vw[1];
        dfs(dfs,1,-1);
        int ans = 0;
        for(int i = 2; i <= n; ++i) if(leaf[i]) ans += 1;
        cout<<ans;
    }
    
    • 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

    C. k-Tree

    Problem - C - Codeforces

    问题描述:一个满k叉树,对于一个节点来说,它到它k个节点的边权分为1…k。求从根节点出发,到路径和为n且存在一条大于等于d的边权的个数。

    思路:像是数字三角形的那个dp,数据范围很小。而且只有一个特殊状态,是否大于d了。设状态为从根节点到一个节点的路径和为i时,存在了大于d的边和不存在大于d的边。

    状态转移方程:
    F ( i , j ) = { w < d F ( i , 0 ) + = F ( i − w , 0 ) ; F ( i , 1 ) + = F ( i − w , 1 ) w ≥ d F ( i , 1 ) + = F ( i − w , 1 ) + F ( i − w , 0 ) w 为 < u , v > 的边权 F(i,j) = {w<dF(i,0)+=F(iw,0);F(i,1)+=F(iw,1)wdF(i,1)+=F(iw,1)+F(iw,0) \\ w为的边权 F(i,j)={w<dF(i,0)+=F(iw,0);F(i,1)+=F(iw,1)wdF(i,1)+=F(iw,1)+F(iw,0)w<u,v>的边权
    边界:
    F ( 0 , 0 ) = 1 F(0,0) = 1 F(0,0)=1
    目标:
    F ( n , 1 ) F(n,1) F(n,1)
    注意: 如果w > i那这个i - w就是负数,不合法,因此遍历的应该是min(w,i)

    代码:

    const LL mod = 1000000007;
    LL f[N][N];
    void inpfile();
    void solve() {
        int n,k,d; cin>>n>>k>>d;
        f[0][0] = 1;
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= min(i,k); ++j) {
                if(j < d) f[i][0] += f[i - j][0], f[i][1] += f[i-j][1];
                else f[i][1] += f[i-j][1] + f[i-j][0];
                f[i][0] %= mod;
                f[i][1] %= mod;
            }
        }
        cout<<f[n][1];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    【微信小程序】条件渲染和列表渲染
    OTA设计思路
    Redis中的慢查询日志(一)
    仿游戏热血江湖游戏类22(得到物品基本攻击力2)
    第67步 时间序列建模实战:ARIMA建模(Stata)
    关于语言大模型的八大论断
    力扣 (LeetCode) LeetCode HOT 100
    docker数据卷详细讲解及数据卷常用命令
    vue使用高德地图-进行显示地图和查询天气
    网络面试一百问<待整理>
  • 原文地址:https://blog.csdn.net/qq_63432403/article/details/132713623