首页 > 其他分享 >『Solution』Codeforces 372D Choosing Subtree is Fun

『Solution』Codeforces 372D Choosing Subtree is Fun

时间:2024-05-06 21:44:46浏览次数:21  
标签:dep Subtree 372D top Codeforces son int dfn maxn

首先这个 \(k\) 的限制不是很好入手,考虑先从如果选取了区间 \([l, r]\) 来入手。
那么此时连通块最小的 \(siz\) 就是把这些点拎出来建虚树对应在原树上的所有点。

那么这个有个结论,考虑按 \(\operatorname{dfn}\) 序排序后的点为 \(p_0\sim p_{k - 1}\),那么对应的最小 \(sz\) 就是 \(\frac{\sum\limits_{i = 0}^{k - 1} \operatorname{dis}(p_i, p_{(i + 1)\bmod k})}{2} + 1\)。
考虑证明,首先考虑边的数量,那么一个边如果会在虚树中,肯定满足子树内有关键点子树外也有关键点,那么子树内和子树外的 \(\operatorname{dis}\) 都不会经过这条边,只有是这里面 \(\operatorname{dfn} = \min \operatorname{or} \max\) 时会走出去,那么就会被算上 \(2\) 次,\(\times \frac{1}{2}\) 即可。
那么点数就 \(=\) 边数 \(+ 1\)。

同时对于 \([l, r]\) 的答案,可以知道一定 \(\le\) \([l, r + 1]\) 的答案。
于是可以考虑用双指针扫过去。
中途 \(\operatorname{dfn}\) 排序的维护可以用 set 处理。

时间复杂度 \(\mathcal{O}(n\log n)\)。

代码
#include<bits/stdc++.h>
const int maxn = 1e5 + 10;
std::vector<int> son[maxn];
int fa[maxn], dep[maxn], siz[maxn];
void dfs1(int u) {
   dep[u] = dep[fa[u]] + 1;
   siz[u] = 1;
   for (int &v : son[u]) {
      fa[v] = u, son[v].erase(std::find(son[v].begin(), son[v].end(), u));
      dfs1(v);
      siz[u] += siz[v], siz[v] > siz[son[u][0]] && (std::swap(son[u][0], v), 1);
   }
}
int dfn[maxn], dfp[maxn], top[maxn], dt;
void dfs2(int u) {
   dfp[dfn[u] = ++dt] = u;
   for (int v : son[u]) {
      top[v] = v == son[u][0] ? top[u] : v;
      dfs2(v);
   }
}
inline int lca(int x, int y) {
   while (top[x] != top[y]) {
      if (dfn[x] < dfn[y]) std::swap(x, y);
      x = fa[top[x]];
   }
   return dep[x] < dep[y] ? x : y;
}
inline int dis(int x, int y) {
   return dep[x] + dep[y] - 2 * dep[lca(x, y)];
}
std::set<int> s;
int tot;
inline void add(int x, int v) {
   if (v == 1) s.emplace(dfn[x]);
   std::set<int>::iterator it = s.find(dfn[x]), it1 = it, it2 = it;
   it2++;
   int y, z;
   if (it1 == s.begin()) y = dfp[*s.rbegin()];
   else y = dfp[*--it1];
   if (it2 == s.end()) z = dfp[*s.begin()];
   else z = dfp[*it2];
   tot += v * (dis(x, y) + dis(x, z) - dis(y, z));
   if (v == -1) s.erase(it);
}
int main() {
   int n, k;
   scanf("%d%d", &n, &k);
   for (int i = 1, x, y; i < n; i++) {
      scanf("%d%d", &x, &y);
      son[x].push_back(y), son[y].push_back(x);
   }
   dfs1(1), top[1] = 1, dfs2(1);
   int ans = 0;
   for (int i = 1, j = 1; i <= n; i++) {
      add(i, 1);
      while (tot / 2 + 1 > k) add(j++, -1);
      ans = std::max(ans, i - j + 1);
   }
   printf("%d\n", ans);
   return 0;
}

标签:dep,Subtree,372D,top,Codeforces,son,int,dfn,maxn
From: https://www.cnblogs.com/rizynvu/p/18175997

相关文章

  • 『Solution』Codeforces 1970B Exact Neighbours
    Easy没什么启发性,直接考虑Medium。考虑到\(a_1=0\),那么\(1\)明显直接和自己配对就行,考虑分配到一个特殊的位置\((1,1)\)。接下来考虑如果还有\(a_i=0\),那么明显\(i\)也是和自己配对,此时因为这是无关紧要的就可以离特殊的\((1,1)\)尽量远一点,也就是让\(x\)坐......
  • Codeforces Round 943 (Div. 3)
    CodeforcesRound943(Div.3)A.Maximize?题意:给定x,求一个范围在[1,x)的数字y,内使得gcd(x,y)+y最大,输出任意的y思路:数据范围很小,暴力枚举即可voidsolve(){intx;cin>>x;inty=1,ma=0;for(inti=1;i<x;i++){intres=__gc......
  • Codeforces 1129E Legendary Tree
    考虑让选取的集合更加特殊,不妨就让\(S=\{x\}\)。那么这个时候能发现询问\((S=\{x\},T,v)\)得到的就是以\(x\)为根时\(v\)的子树内\(T\)中的点的数量。考虑定个根,不妨为\(1\),同时令\(S=\{1\}\)。那么询问\((\{1\},\{1,\cdotsn\}\backslash\{1,x\},x)......
  • Codeforces Round 943 (Div. 3)
    无伤但没速通,然后被hack两个题,直接从rk90掉到rk114514+了,我是真他妈的服了。特此纪念A暴力枚举秒了。B二分答案秒了C他妈脑子抽了,差一步完全整解。我们发现只要确定\(a_{\mathbf{1}}\)那么你直接不断加\(x_i\)就能求出\(a_i\),但是直接这么搞过不了样例,观察一下,然后直......
  • Codeforces 452F Permutation
    对于\(a,b,\frac{a+b}{2}\)肯定是需要固定下一些变量来考虑的。考虑固定下\(c=\frac{a+b}{2}\),考虑\(a,b\)。那么这样的\(a,b\)肯定满足\(a-c=b-c,a\not=b\),那么以\(c\)为中心,\(a,b\)就是对称的。用\(s_i=0,1\)来表示\(i\)是在\(c\)的......
  • Codeforces 1044F DFS
    考虑到存在方案使得以\(u\)为起点还能走出原树的条件。能发现是以\(u\)为根,在原树上新添加的边只会是返祖边而不会有横叉边。这是因为如果有横叉边就肯定会在遍历到一边的点后先通过这条边走到另一边的点,就算上了这条边就不是原树了。那么考虑\((x,y)\),合法的\(u\)需要......
  • Educational Codeforces Round 165 (Rated for Div. 2) C. Minimizing the Sum题解
    题意CodeforcesRound809(Div.2)D1.ChoppingCarrots(EasyVersion)给你两个整数\(n(1\len\le3e5),k(0\lek\le10)\),一个数组\(a(1\lea_i\le10^9)\)。你可以进行如下操作最多\(k\)次:选定一个数\(i(1\lei\len)\),让其变为相邻的数(变为\(a_{i-1},a_{i......
  • Codeforces Round 942 (Div. 2) (A - E)
    A.ContestProposal如果\(a_i>b_i\),则答案加一,令\(\foralli\in[i+1,n],\a_i\leftarrowa_{i-1}\)。submissionB.CoinGames题意:\(n\)枚硬币围成一圈,给出初始硬币状态,每取出一枚正面朝上的硬币并翻转相邻的两枚,没有正面则对方获胜,问先手胜负。令当前正面硬......
  • Codeforces Round 942 (Div. 2)
    CodeforcesRound942(Div.2)A.ContestProposal题意:有n个题目,每个题目的难度为a[i],要求每个题目的难度不大于对应的b[i],每次可以添加一个题目并且删去最难的题目,求最多能添加几个题目思路:暴力枚举即可,只要a[i]大于b[i],就把a[n]改为b[i],然后重新排序voidsolve(){int......
  • Codeforces Round 942 Div.2 题解
    蹭个热度,挽救一下cnblogs蒸蒸日上的阅读量。Q:你是手速狗吗?A:我觉得我是。2A因为选的\(w\)一定可以让它合法,一次操作可以看作\(a\)数组向右平移一位。枚举操作次数后暴力判断即可。#include<bits/stdc++.h>voidwork(){ intn; std::cin>>n; std::vector<......