树形dp,CF 1778F - Maximizing Root

一、题目

1、题目描述

2、输入输出

2.1输入

2.2输出

3、原题链接

1778F - Maximizing Root


二、解题报告

1、思路分析

2600的题,不过还算友好

先思考我们最终的答案是什么样的?

a[0] * a[0]的某个因子

而结点的值都在1000以内,所以因子个数不超过32(这个可以打表看一下)

然后dp味还是比较强的

我们先预处理几个数组,gcd[i][j]即 i 和 j 的gcd

divs[i],即 i 的所有因子,降序排序

ceilSqrt[i],即 j ^ 2 = i的最小 j 

每个样例预处理一次:

subG[u],即 子树u的gcd


我们从大到小去遍历a[0]的因子,只要有能够在k步以内调整出来的,我们就求答案输出

dfs(u, fa, d) 表示 子树 u 的gcd是否可以调整为 d 的倍数

如果 subG[u] % d == 0,不需要调整,直接返回

如果 subG[u] * subG[u] % d == 0,那么调整一次就能得到

如果 u 为 叶子 或者 a[u] * a[u] % d != 0,那么无法调整,直接返回

否则,看 u 的所有子树 v 是否都能调整出 ceilSqrt[d] 的倍数,如果可以,那么每个子树都可以贡献出ceilSqrt[d],我们再对u 调整一次就能得到d的倍数

详细实现看代码

2、复杂度

时间复杂度: O(32N)空间复杂度:O(32N)

3、代码详解

 ​
#include <bits/stdc++.h>
using i64 = long long;
using i128 = __int128;
using PII = std::pair<int, int>;
using PIII = std::pair<int, PII>;
constexpr int inf = 1e9 + 7, P = 1e9 + 7;
constexpr int N = 1000;

std::vector<int> divs[N + 1];
int gcd[N + 1][N + 1];
int ceilSqrt[N + 1];

void init() {
    for (int i = N; i; -- i) {
        for (int j = i; j <= N; j += i)
            divs[j].push_back(i);
        for (int j = 1; j <= N; ++ j)
            gcd[i][j] = std::gcd(i, j);
        ceilSqrt[i] = 1;
        int x = i;
        for (int p = 2; p * p <= x; ++ p) {
            while (x % (p * p) == 0) {
                ceilSqrt[i] *= p;
                x /= (p * p);
            }
            if (x % p == 0) {
                ceilSqrt[i] *= p;
                x /= p;
            }
        }
        if (x > 1)
            ceilSqrt[i] *= x;
    }
}

void solve() {
    int n, k;
    std::cin >> n >> k;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) std::cin >> a[i];
        std::vector<std::vector<int>> g(n);
    for (int i = 1, u, v; i < n; ++ i) {
        std::cin >> u >> v;
        -- u, -- v;
        g[u].push_back(v), g[v].push_back(u);
    }

    if (!k) {
        std::cout << a[0] << '\n';
        return;
    }

    std::vector<int> subG(n);

    auto dfs0 = [&](auto&& self, int u, int fa) -> void {
        subG[u] = a[u];
        for (int v : g[u]) 
            if (v != fa) {
                self(self, v, u);
                subG[u] = gcd[subG[u]][subG[v]];
            }
    };

    dfs0(dfs0, 0, -1);

    int cnt = 0;

    auto dfs = [&](auto&& self, int u, int fa, int d) -> void {
        if (subG[u] % d == 0)
            return;

        if (subG[u] * subG[u] % d == 0) {
            ++ cnt;
            return;
        }

        if (g[u].size() == 0 || a[u] * a[u] % d) {
            cnt = inf;
            return;
        }

        for (int v : g[u]) 
            if (v != fa) 
                self(self, v, u, ceilSqrt[d]);
        ++ cnt;
    };

    for (int d : divs[a[0]]) {
        cnt = 0;
        for (int v : g[0])
            dfs(dfs, v, 0, d);
        if (cnt < k) {
            std::cout << a[0] * d << '\n';
            return;
        }
    }
}


int main(int argc, char** argv) {
    std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
    init();
    int _ = 1;
    std::cin >> _;
    while (_ --)
        solve();
    return 0;
}

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个六子棋博弈树的α-β剪枝算法的Python代码: ```python import numpy as np # 博弈树节点 class Node: def __init__(self, state, depth, alpha=-np.inf, beta=np.inf): self.state = state self.depth = depth self.alpha = alpha self.beta = beta self.children = [] def is_terminal(self): return self.depth == 0 or self.state.is_game_over() def evaluate(self): # 计算当前节点的估价值 return self.state.evaluate() def generate_children(self): # 生成当前节点的所有子节点 for move in self.state.get_legal_moves(): new_state = self.state.apply_move(move) child = Node(new_state, self.depth - 1) self.children.append(child) def get_best_child(self): # 获取当前节点的最佳子节点 best_child = None if self.children: best_child = self.children[0] for child in self.children: if child.alpha >= best_child.alpha: best_child = child return best_child # MiniMax算法 def minimax(node, maximizing_player): if node.is_terminal(): return node.evaluate() if maximizing_player: value = -np.inf for child in node.children: value = max(value, minimax(child, False)) node.alpha = max(node.alpha, value) if node.beta <= node.alpha: break return value else: value = np.inf for child in node.children: value = min(value, minimax(child, True)) node.beta = min(node.beta, value) if node.beta <= node.alpha: break return value # α-β剪枝算法 def alphabeta(node, depth, alpha, beta, maximizing_player): if depth == 0 or node.is_terminal(): return node.evaluate() if maximizing_player: value = -np.inf for child in node.children: value = max(value, alphabeta(child, depth - 1, alpha, beta, False)) alpha = max(alpha, value) if beta <= alpha: break return value else: value = np.inf for child in node.children: value = min(value, alphabeta(child, depth - 1, alpha, beta, True)) beta = min(beta, value) if beta <= alpha: break return value # 六子棋游戏状态 class GameState: def __init__(self): self.board = np.zeros((6, 6), dtype=int) self.current_player = 1 def is_game_over(self): # 判断游戏是否结束 if self.get_winner() is not None: return True return False def get_winner(self): # 判断游戏胜负 for i in range(6): if np.sum(self.board[i]) == 6: return 1 elif np.sum(self.board[i]) == -6: return -1 for i in range(6): if np.sum(self.board[:, i]) == 6: return 1 elif np.sum(self.board[:, i]) == -6: return -1 if self.board.trace() == 6 or np.fliplr(self.board).trace() == 6: return 1 elif self.board.trace() == -6 or np.fliplr(self.board).trace() == -6: return -1 return None def evaluate(self): # 计算当前游戏状态的估价值 winner = self.get_winner() if winner is not None: return winner * np.inf else: return np.sum(self.board) def get_legal_moves(self): # 获取当前玩家的合法落子位置 moves = [] for i in range(6): for j in range(6): if self.board[i][j] == 0: moves.append((i, j)) return moves def apply_move(self, move): # 更新游戏状态 new_state = GameState() new_state.board = np.copy(self.board) new_state.current_player = -self.current_player i, j = move new_state.board[i][j] = self.current_player return new_state # 初始化博弈树 def initialize_tree(state, depth): root = Node(state, depth) root.generate_children() return root # 选择最佳落子位置 def select_move(state, depth): root = initialize_tree(state, depth) for child in root.children: minimax(child, False) #alphabeta(child, depth - 1, -np.inf, np.inf, False) # 使用α-β剪枝算法 root.alpha = max(root.alpha, child.alpha) best_child = root.get_best_child() return best_child.state.get_last_move() # 测试 state = GameState() while not state.is_game_over(): if state.current_player == 1: move = select_move(state, 3) state = state.apply_move(move) else: move = tuple(map(int, input("请输入黑方落子位置:").split())) state = state.apply_move(move) print(state.board) print("游戏结束,胜利者为", state.get_winner()) ``` 在代码中,`Node`类表示一个博弈树节点,`GameState`类表示一个六子棋游戏状态。`minimax`函数是标准的MiniMax算法,`alphabeta`函数是α-β剪枝算法。`initialize_tree`函数用于初始化博弈树,`select_move`函数用于选择最佳落子位置。在测试部分,程序会先使用AI自动落子,然后等待人类玩家输入落子位置,直到游戏结束。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EQUINOX1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值