首页 > 其他分享 >AT_agc009_d [AGC009D] Uninity

AT_agc009_d [AGC009D] Uninity

时间:2024-11-24 21:44:00浏览次数:5  
标签:ch 一个点 AGC009D int agc009 Uninity 两棵树 点权

这题看完题解后迟迟不下手写代码,因为这道题实在是太厉害了!

考虑对于一棵树手玩这个过程,发现如果一个点要作为中间的一个节点,它肯定会挂上周围的所有点所在的树,当然它之后挂的点除外。这事实上是一个点分树的过程,那么该问题就是求最大深度最小的点分树,发现并不好做。

好在它刚刚告诉我答案是 \(\log n\) 级别的,这样似乎会好做一些。现在彻底清除刚刚自己思考的废料,只保留刚刚挖掘出的答案的级别,继续思考性质,发现一棵树能作为一棵 Uninity \(k\) 的树的充要条件是给每个点赋一个权,对于权相同的两个点的路径上必须得存在一个点权大于自己的点,切整棵树的点权最大值为 \(k\),而一个点在问题中挂了若干棵 Uninity \(k\) 的数,则这个点点权为 \(k+1\)。

这个性质貌似可以在考虑两棵树的时候想到,因为要想把两棵树连起来,显然要有一个点能让这两棵树挂在一起,而这两棵树挂的点显然大于这两棵树里的点的点权。

所以现在问题转化了,我们只需要求使得最大值最小的合法的点权分配方式即可。考虑贪心的子树转移,设 \(S_u\) 表示子树内有哪些点权在到 \(u\) 的路径中不存在比该点权大的点,那么当前点权 \(a_u\) 现在不在任意一个 \(S_{son_u}\) 里面出现,而如果有至少两棵子树的集合中存在相同的点权,那么当前点权 \(a_u\) 得保证大于这些点权,因为需要满足这两个路径中存在比自己大的点权。

直接二进制转移即可。

代码:

#include <bits/stdc++.h>
#define rep(i, l, r) for (int i (l); i <= r; ++ i)
#define rrp(i, l, r) for (int i (r); i >= l; -- i)
#define pii pair <int, int>
#define eb emplace_back
#define inf 1000000000
using namespace std;
constexpr int N = 1e5 + 5, M = 310, P = 1e9 + 7;
typedef unsigned long long ull;
typedef long long ll;
inline ll rd () {
  ll x = 0, f = 1;
  char ch = getchar ();
  while (! isdigit (ch)) {
    if (ch == '-') f = -1;
    ch = getchar ();
  }
  while (isdigit (ch)) {
    x = (x << 1) + (x << 3) + ch - 48;
    ch = getchar ();
  }
  return x * f;
}
void add (int &x, int y) {
  (x += y) >= P ? (x -= P) : (x); 
}
int qpow (int x, int y) {
  int ret = 1;
  for (; y; y >>= 1, x = x * x % P) if (y & 1) ret = ret * x % P;
  return ret;
}
int n;
vector <int> e[N];
int ans, f[N];
void dfs (int u, int fa) {
  int s = 0, p = 0;
  for (auto v : e[u]) {
    if (v == fa) continue;
    dfs (v, u);
    rep (i, 0, 20) {
      if ((s >> i & 1) && (f[v] >> i & 1)) p = max (p, i + 1); 
    } s |= f[v];
  }
  while (s >> p & 1) ++ p;
  ans = max (ans, p);
  f[u] = s;
  f[u] |= 1 << p;
  f[u] = (f[u] >> p) << p;
}
int main () {
  // freopen ("1.in", "r", stdin);
  // freopen ("1.out", "w", stdout);
  n = rd ();
  rep (i, 2, n) {
    int u = rd (), v = rd ();
    e[u].eb (v), e[v].eb (u);
  }
  dfs (1, 0);
  cout << ans;
}

标签:ch,一个点,AGC009D,int,agc009,Uninity,两棵树,点权
From: https://www.cnblogs.com/lalaouyehome/p/18566462

相关文章

  • AGC009B 题解
    注意到如果把每一对胜者败者连边,可以得到一颗树:(例子)但是因为胜者每次只能和一个败者打,所以要用类似多叉转二叉的方法,让有不止一个孩子的节点变成有一个孩子和一个虚点。如图,原来\(1\)有三个儿子\(2,3,4\),通过转换,变成了上图。上图可以直接变成对战图(\(x2\tox1\to1)\):(原......
  • [AGC009C] Division into Two
    先假定\(A\leB\),然后先判断无解,如果\(a_{i+2}-a_i<B\),无论怎么分配都是不合法的,直接判掉。然后考虑dp,\(f_i\)表示选了前\(i\)个数,其中第\(i\)个数是归为\(A\)集合的方案数。其中不难发现可转移的状态是一段区间,状态\(f_j\)可以转移仅当\(a_i-a_j\geA\)且\(a_......
  • [AGC009B] Tournament 题解
    思路考虑树形\(\text{dp}\)。我们将每个人与把自己淘汰的人连边。得到一颗以一为根的树。由于我们需要求出必须赢的场数最多的那位选手,至少要赢多少场。考虑最多的限制。可以使用树型动态规划。每一次两个人比赛的代价为:\[dp_i=\max(dp_i,dp_j)+1\]这样就达成了最多的限......
  • 「解题报告」AGC009E Eternal Average
    笑了,题意转换的思路大致都是对的,不知道为啥猜成与题解结论完全相反的结论了。首先考虑将这个过程看做是一棵满\(k\)叉树,其中有\(n+m\)个叶子,\(n\)个叶子为\(0\),\(m\)个叶子为\(1\)。不难发现,如果一个\(1\)的深度为\(x\),那么它对最后的数造成的贡献为\(\frac{1}{k^x......
  • AGC009
    吗了为什么我T2没开longlong。吗了为什么我T4没想出来。果然还是太菜。正在思考我现在在做什么,和意义在哪里。最近几天高强度AGC好像打的心态有点爆炸。明天如果......
  • AGC009E口胡
    赛时应该口胡了个大概,可惜没有转化成更纯粹的问题。问题可以看做有多少不同的\(x\)满足\(x=\sum_{i=1}^{m}(\frac{1}{k})^{a_i},1-x=\sum_{i=1}^{n}(\frac{1}{k})^{b_i......
  • AT2294 [AGC009E] Eternal Average
    题目传送门考虑求值的过程,容易发现我们会形成一颗\(k\)叉树,然后最后的总和是每个\(1\)点对应的深度的\(\frac{1}{k}\)次幂和。容易发现在同一层有\(k\)个同样的点可以用......