首页 > 其他分享 >CF868E Policeman and a Tree

CF868E Policeman and a Tree

时间:2023-10-26 19:22:43浏览次数:31  
标签:std Policeman int auto CF868E Tree ++ vector dis

感觉,好自然啊!

想法 dp,想办法分解这个博弈的过程。发现警察会从一片叶子到另一片叶子,在叶子抓住小偷时所有小偷可以全树乱走。因此 dp:\(f_{u, i}\) 表示警察位于 \(u\),全树剩余 \(i\) 个小偷时的答案。

因为两边都绝对理性,小偷在警察离开叶子后不会移动并位于多片叶子上。考虑小偷的决策确定之后的情况。警察要枚举叶片 \(v\),转移形如 \(f_{u, x} = \min\{f_{v, x-i} + dis(u, v)\}\),\(i\) 是 \(v\) 处小偷的个数。小偷需要最大化这个值。

转移可以用背包,也可以直接二分答案。二分答案时,每片叶子上小偷的数目有上限,加起来看是否大于等于 \(x\) 就行了。

对于小偷有初始情况,不过是出现了多棵子树,小偷不能全树跑。故每片叶子上小偷的数目的上限会不大一样。用一样的方法做就行了。

我们当时,不明白如何处理双方绝对理性这个条件。如今看来,不过是交替转移。能确定一方的策略后,另一方让其最劣,也是可以的。

#include <bits/stdc++.h>

const int inf = 1e9 + 7;

int main() {
  int n, s, m; scanf("%d", &n);
  std::vector<std::vector<int>> dis(n, std::vector<int> (n, inf));
  std::vector<std::vector<std::pair<int, int>>> e(n);
  std::vector<int> deg(n), siz(n);
  for (int i = 1, u, v, w; i < n; i++) {
    scanf("%d %d %d", &u, &v, &w), --u, --v;
    dis[u][v] = dis[v][u] = w, ++deg[u], ++deg[v];
    e[u].push_back({v, w}), e[v].push_back({u, w});
  }
  for (int i = 0; i < n; i++)
    dis[i][i] = 0;
  for (int k = 0; k < n; k++)
    for (int i = 0; i < n; i++)
      for (int j = 0; j < n; j++)
        dis[i][j] = std::min(dis[i][j], dis[i][k] + dis[k][j]);
  scanf("%d %d", &s, &m), --s;
  for (int i = 0, u; i < m; i++) scanf("%d", &u), --u, ++siz[u];
  std::vector<std::vector<int>> f(n, std::vector<int> (m + 1));
  std::vector<int> leaf;
  for (int i = 0; i < n; i++) if (deg[i] == 1) leaf.push_back(i);
  auto chk = [&](int u, int x, int lim) -> bool {
    int sum = 0;
    for (auto v : leaf) if (v != u) {
      int p = x;
      for (; p >= 1 && f[v][x - p] + dis[u][v] < lim; --p) ;
      sum += p;
    }
    return sum >= x;
  } ;
  for (int j = 1; j <= m; j++) {
    for (auto u : leaf) {
      int l = 0, r = 1e9; 
      while (l <= r) {
        int mid = l + r >> 1;
        if (chk(u, j, mid)) f[u][j] = mid, l = mid + 1;
        else r = mid - 1;
      }
    }
  }
  std::vector<int> bl(n);
  auto dfs = [&](auto self, int u, int f) -> void {
    for (auto [v, w] : e[u]) if (v != f) 
      bl[v] = bl[u], self(self, v, u), siz[u] += siz[v];
  } ;
  for (auto [u, w] : e[s]) bl[u] = u, dfs(dfs, u, s);
  auto chk2 = [&](int lim) -> bool {
    std::vector<int> res(n);
    int sum = 0;
    for (auto [u, w] : e[s]) res[u] = siz[u];
    for (auto u : leaf) if (u != s) {
      int j = res[bl[u]];
      for (; j && dis[s][u] + f[u][m - j] < lim; --j) ;
      sum += j, res[bl[u]] -= j;
    }
    return sum >= m;
  } ;
  int l = 0, r = 1e9, ans = 0;
  while (l <= r) {
    int mid = l + r >> 1;
    if (chk2(mid)) ans = mid, l = mid + 1;
    else r = mid - 1;
  }
  printf("%d\n", ans);
}

标签:std,Policeman,int,auto,CF868E,Tree,++,vector,dis
From: https://www.cnblogs.com/purplevine/p/solution-CF868E.html

相关文章

  • P6109 [Ynoi2009] rprmq1 题解-猫树+Segment Tree Beats
    20231025P6109[Ynoi2009]rprmq1题解-猫树+SegmentTreeBeats不愧是学长出的题。。。让我更深刻地理解了猫树。Statement传送门有一个\(n\timesn\)的矩阵\(a\),初始全是\(0\),有\(m\)次修改操作和\(q\)次查询操作,先进行所有修改操作,然后进行所有查询操作。一次修......
  • 75th 2023/10/6 k-D Tree
    附上一图:按维度分级,每次轮换用哪个维度即可oi中大多为2维这就是我对它的全部理解了结构与线段树几乎相同分左右结点时取当前区间段的中位数因而每一个节点都不同于线段树的表示范围它表示的是一个确确实实的节点的值访问前可以维护一个节点及它的子树的维度上下界以减少询......
  • CF1572F Stations 题解-Segment Tree Beats
    20231025CF1572FStations题解-SegmentTreeBeats吉司机线段树好题!!!CF3400。传送门Statement有\(n\)个广播站,第\(i\)个广播站高度为\(h_i\),范围为\(w_i\)。初始\(h_i=0,w_i=i\)。广播站\(i\)能向广播站\(j\)传递消息,当且仅当\(i\lej\lew_i\),且\(h_i>\max\lim......
  • CF1467E Distinctive Roots in a Tree
    突然发现深究一些树上问题还是挺有意思的哈。显然对于同一种权值的任意两个结点,其两端的部分都是不合法的。维护两个标记表示子树内均不合法与子树外均不合法即可。但相同权值的点对数量是\(O(n^2)\)的,我们要优化这个过程。发现很多点对都是无用的。DFS下去,遇到一个\(x\)......
  • Jquery 下拉树下面是一个使用Combotree组件的简单案例:
    1、html <!DOCTYPEhtml><html><head><title>Combotree使用案例</title><linkrel="stylesheet"type="text/css"href="https://cdn.jsdelivr.net/npm/jquery-combotree/dist/jquery.combotree.min.css"......
  • gitee与SourceTree的安装使用
    git可视化管理工具SourceTree安装教程:http://wed.xjx100.cn/news/174839.html?action=onClickgitee可视化管理工具SourceTree安装使用教程:https://blog.csdn.net/wan369282913/article/details/131858067这两篇文章结合着看,第一步下载git,第二步下载sourcetree,第三步用git生成公钥......
  • 版本管理客户端工具SourceTree
      [使用]1.设置SSH客户端工具>选项 设置OpenSSH, SSH密钥这一栏自然会去选择当前用户下的.ssh目录下的id_rsa这个私钥: ......
  • CF1003E Tree Constructing
    很trivial的构造题首先上来判掉一些显然无解的情况,然后考虑既然最后直径长为\(d\)那么不妨先搞一条长度为\(d\)的链来考虑在链上接一些点使得直径不会变长,对于链上的某个点,它最多能接上的链的长度就是它到两个端点距离的最小值不妨设计递归函数求解,设solve(x,dis,lim)表示在\(x......
  • CF723F st-Spanning Tree
    小清新贪心+分类讨论,因为边的数组开小了WA了好久……首先我们贪心地选出不包含\(s,t\)的边,用这些边尽量地将除了\(s,t\)外的\(n-2\)个点连通接下来考虑每个连通块,由于题目保证图初始连通,因此只有三种情况,即要么其中仅有和\(s\)相连的边;仅有和\(t\)相连的边;或者同时有向\(s,t\)连......
  • [ABC207F] Tree Patrolling 题解
    [ABC207F]TreePatrolling弱智DP题,设\(f(i,j,0/1/2)\)表示在点\(i\),子树中有\(j\)个点被覆盖,且\(i\)点自身状态是未被覆盖/被自身覆盖/被某个儿子覆盖,然后树上背包更新就行了。代码:#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongconstintmo......