首页 > 其他分享 >CF1039D You Are Given a Tree

CF1039D You Are Given a Tree

时间:2024-09-15 21:35:37浏览次数:1  
标签:Given int siz top Tree CF1039D MAXN MS max

CF1039D You Are Given a Tree

咋其他题解都带根号?根号是坏文明,这里是俩 \(\log\) 做法,能够跑到 \(300\) ms 以内。

首先考虑暴力贪心,从叶子向根合并,可以取出一条链的时候就取出来,否则就连一条尽可能长的链往上合并。

具体的设 \(f_{x,i}\) 为当 \(k=i\) 时,在 \(x\) 处能取出的最长链。那么也就是从 \(f_{y,i},y\in son(x)\) 中选出最大值和次大值,如果连起来长度 \(\geq i\),那么答案加一,将 \(f_{x,i}\) 置为 \(0\);否则 \(f_{x,i}\) 等于儿子中最大值加一。这东西肯定是 \(O(n^2)\) 的,考虑优化。


记 \(siz_x\) 是 \(x\) 及其子树大小,\(d_x\) 为 \(x\) 子树中最深点与 \(x\) 的距离加一。

有一个比较常见的 trick 是树剖,然后直接继承重儿子的 \(f\) 值,从这点上看这个做法可能类似 dsu on tree。

那么现在操作变成了,继承时对全局加一,然后在轻儿子中找到最大值和次大值,进行更新。

首先不难发现,子树中选出一条最长链的长度是不超过子树大小的,换句话来说,当 \(i>siz_x\),\(f_{x,i}=d_x\)。

那么也就意味着,我们记 \(MS(x)\) 为 \(x\) 的轻儿子中 \(siz\) 最大值,那么对所有 \(i>MS(x)\) 的更新都是相同的。

然后我们发现,经典结论所有轻儿子的 \(siz\) 和是 \(O(n\log n)\) 级别的;所有长度的答案和是调和级数,是 \(O(n\ln n)\) 级别的。

如果我们可以对 \(i>MS(x)\) 的修改打包,每次只暴力更新 \(i\leq MS(x)\) 的位置和所有可以贡献到答案的位置,时间复杂度就是对的。

而由于我们需要找出一段区间中所有满足一定条件的位置,所以考虑线段树。


首先一条链的线段树的大小是 \(siz_{top}\) 的(\(top\) 表示这条链的链头),这个东西的和自然也是 \(O(n\log n)\) 级别的。

我们可以在线段树上维护区间 \(f_{x,i}-i\) 最大值,具体的怎么判断是否可以贡献到答案就是三条链里取两条长的,不多说了。

\(i\leq MS(x)\) 的部分就直接先暴力把所有轻儿子的线段树上维护的值取出来,然后预处理取完 \(\max\) 后再暴力递归到当前线段树的叶子节点修改。

\(i> MS(x)\) 打包修改的部分就相当于如果区间中有可以贡献到答案的位置就继续递归,否则打一个区间加、区间取 \(\max\) 的 tag,值得注意的是我们先前维护的 \(f_{x,i}-i\) 最大值可能可以被区间取的 \(\max\) 影响,比方说区间 \(\max\) 是 \(s\) 的话,那个最大值是要和 \(s-l\)(\(l\) 是区间的左端点)取 \(\max\) 的。

那么总时间复杂度就是 \(O(n\log^2 n)\) 的,实际上你修改都是一起修改的,所以大概常数还很小,薄纱一众根号做法。

另外可以直接修改 \(siz_x\) 的定义为 \(x\) 及其子树中选出一条最长链的长度,好像并不会减少时间,只不过更贴近我们的用途。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#define ll long long
using namespace std;
const int MAXN=1e5+10;
int n,d[MAXN],siz[MAXN],h[MAXN],top[MAXN],ans[MAXN];
vector <int> v[MAXN];typedef pair<int,int> P;P f[MAXN];
inline void max(P &x,int y)
{
    if(y>x.first) swap(y,x.first);
    if(y>x.second) x.second=y;
}
inline void max(P &x,P y)
    {max(x,y.first),max(x,y.second);}
namespace segment_tree
{
    #define LS t[t[p].ls]
    #define RS t[t[p].rs]
    int R[MAXN],tot;queue <int> q;
    struct node{int num,add,s,ls,rs,l;}t[MAXN<<5];
    inline int N()
    {
        if(q.empty()) return ++tot;
        int x=q.front();q.pop();return x;
    }
    inline void add(int p,int c,int z)
    {
        t[p].add+=c,t[p].s=max(t[p].s+c,z);
        t[p].num=max(t[p].num+c,t[p].s-t[p].l);
    }
    inline void push_up(int p)
        {t[p].num=max(LS.num,RS.num);}
    inline void push_down(int p)
    {
        add(t[p].ls,t[p].add,t[p].s);
        add(t[p].rs,t[p].add,t[p].s);
        t[p].add=t[p].s=0;return ;
    }
    void build(int l,int r,int p)
    {
        t[p].l=l;if(l==r){t[p].num=-l+1;return ;}
        int mid=(l+r)>>1;
        build(l,mid,t[p].ls=N());
        build(mid+1,r,t[p].rs=N());
        push_up(p);return ;
    }
    void get(int l,int r,int p)
    {
        if(l==r)
        {
            max(f[l],t[p].num+l);
            t[p]={},q.push(p);return ;
        }
        int mid=(l+r)>>1;push_down(p);
        if(t[p].ls) get(l,mid,t[p].ls);
        if(t[p].rs) get(mid+1,r,t[p].rs);
        t[p]={},q.push(p);return ;
    }
    void upd(int l,int r,int p,int x,int S,int D)
    {
        if(l>x&&S<l&&t[p].num+D+1<0)
            {add(p,1,D+1);return ;}
        if(l==r)
        {
            t[p].s=0;if(l<=x)
            {
                int S=f[l].first+max(f[l].second,t[p].num+l)+1;
                if(S>=l) ++ans[l],t[p].num=-l;
                else t[p].num=max(t[p].num,f[l].first-l)+1;
            }
            else ++ans[l],t[p].num=-l;return ;
        }
        int mid=(l+r)>>1;push_down(p);
        upd(l,mid,t[p].ls,x,S,D);
        upd(mid+1,r,t[p].rs,x,S,D);
        push_up(p);return ;
    }
};using namespace segment_tree;
void dfs(int x,int fa=0)
{
    P D={0,0};for(int y:v[x])
    {
        if(y==fa) continue;
        dfs(y,x),max(D,d[y]);
        if(siz[y]>siz[h[x]]) h[x]=y; 
    }
    d[x]=D.first+1;
    siz[x]=max(siz[h[x]],d[x]+D.second);
}
void calc(int x,int fa=0)
{
    if(!h[x])
    {
        if(siz[top[x]]>=2)
            build(2,siz[top[x]],R[x]=N());
        return ;
    }
    top[h[x]]=top[x],calc(h[x],x),R[x]=R[h[x]];
    for(int y:v[x])
        if(y!=fa&&y!=h[x]) top[y]=y,calc(y,x);
    P MD={0,0};int MS=0;
    for(int y:v[x])
    {
        if(y==fa||y==h[x]) continue;
        max(MD,d[y]),MS=max(MS,siz[y]);
        max(f[siz[y]+1],d[y]);
    }
    for(int i=2;i<=MS;++i) max(f[i],f[i-1]);
    for(int y:v[x])
        if(y!=fa&&y!=h[x]&&siz[y]>=2) get(2,siz[y],R[y]);
    int D=MD.first,S=MD.first+MD.second+1;
    upd(2,siz[top[x]],R[x],MS,S,D);
    for(int i=2;i<=MS+1;++i) f[i]={0,0};return ;
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("in.in","r",stdin);
    freopen("out.out","w",stdout);
#endif
    cin.tie(0),cout.tie(0);
    ios::sync_with_stdio(0);
    cin>>n;for(int i=1,x,y;i<n;++i)
        cin>>x>>y,v[x].push_back(y),v[y].push_back(x); 
    dfs(1),top[1]=1,calc(1),ans[1]=n;
    for(int i=1;i<=n;++i) cout<<ans[i]<<'\n';
    return 0;
}

标签:Given,int,siz,top,Tree,CF1039D,MAXN,MS,max
From: https://www.cnblogs.com/int-R/p/18415137/CF1039D

相关文章

  • QTreeView+QStyledItemDelegate实现编辑名称功能
    1.需求描述点击编辑按钮,进入编辑状态,点击确认和取消按钮退出编辑状态(1)  重写代理createEditor函数这个函数是代理触发编辑信号后,自动创建编辑界面的widget对象,覆盖在item上;EmptyTreeItem就是我们自定义的编辑控件,包括输入框,确认和取消按钮;QWidget*TreeTaskDelegate::......
  • QTreeView置顶排序功能实现
    1.需求描述QTreeView先插入的排在上面,并支持手动置顶进行排序,取消置顶;2.实现方案(1)定义排序角色给每一个插入的QStandardItem对象设置一个排序角色,我们用插入时间来设置这个值;enum CustomRole{   QOrderRole=Qt::UserRole+1};构造函数中设置model的排序角色m_......
  • QStandardItem先设置图标再插入QTreeView会影响插入性能
    所有的界面显示都交代理去绘制,否则会影响插入性能;一开始打算将类型图标通过QStandardItem创建时传给QStandardItem,在插入到model中,后来发现这样会降低插入的性能;pItem=newQStandardItem(QIcon(":/foldericon.svg"),info.value("name").toString());改成用代理QStyledItemDel......
  • QTreeView实现搜索功能并且关键字标红
    1、需求描述实现组织树搜索,关键字红色显示;搜索规则,名称匹配显示,没有匹配不显示,子节点匹配,父节点即使没有匹配也显示;2.实现方法(1)top节点名称匹配关键则显示,否则隐藏voidTreeTaskList::SlotFilterChanged(QStringstrText){m_TreeDelegate->setProperty("FilterString"......
  • QTreeView代理QStyledItemDelegate实现按钮的鼠标hover移动和点击响应
    1.需求描述QStyledItemDelegate实现按钮的点击和响应功能,鼠标移动到按钮上,也会显示tooltip提示信息2.实现方法(1)重写editorEvent函数,根据type类型触发不同的响应函数为了实现按钮的响应,需要重写QStyledItemDelegate类的editorEvent函数,并根据插入时设置进去的type类型,判断是......
  • QTreeView代理QStyledItemDelegate实现按钮、图标的绘制
    1.需求描述代理实现按钮图标状态的绘制实现方法(1)重写paint函数,根据type类型绘制案件、文件夹、监控点、视频任务; 为了实现不同的item的样式,需要继承QStyledItemDelegate类型实现TreeTaskDelegate。重写paint函数,根据不同的类型type去绘制不同的按钮和状态;voidTreeTaskD......
  • java中的Map系列的集合HashMap、HashTable、TreeMap以及Collections和Collection的区
    1.Map的特性特性:key键-value值身份证号--->人可以通过key获取到valueMap它的key是唯一的,Map中的key是无序的而且是不重复的value是可以重复的。Map集合的基本方法:Vput(Kkey,Vvalue)添加元素如果put的key存在那么会用新的value的值替换掉原有的value值key......
  • ARM-8 代码还原动态调试之 pstree 多个条件跳转
    402600: b9405360 ldr w0,[x27,#80]//w0=show_parents,调试确认为show_parents402604: f9400774 ldr x20,[x27,#8]//x20=list402608: 7100001f cmp w0,#0x0//show_parents?=040260c: b9401fe0 ldr w0,[sp,#28]//......
  • [ARC101E] Ribbons on Tree 题解
    [ARC101E]RibbonsonTree题解其实算一道好题了。首先考虑不相关的simple的dp。平凡的想法是设\(dp_{i,j}\)表示\(i\)子树内有\(j\)个点还需要向上转移的方案数。转移式大概是个\(dp_{x,i+j}=dp_{y,i+j-1}+(dp_{p,i-1}+dp_{q,j-1})\)之类的东西。这样的dp是\(O(......
  • WPF 什么时候 VisualTreeHelper.GetDescendantBounds 将返回无穷大
    本文将和大家介绍在什么情况下WPF将会在调用VisualTreeHelper.GetDescendantBounds方法时,返回一个无穷大的范围尺寸在WPF的容器控件的里层元素的RenderTransform包含NaN将会导致对上层容器调用VisualTreeHelper.GetDescendantBounds返回无穷大返回的矩形范围是-∞,......