zoukankan      html  css  js  c++  java
  • 【题解】Lomsat gelral [CF600E]

    【题解】Lomsat gelral [CF600E]

    【前言】

    写完 ( ext{Dsu on tree}) 后大致浏览了网上的题解,常见做法有以下几种:

    • ( ext{Dsu on tree})(占大多数,毕竟是板子)

    • 线段树合并(空间巨大)

    • (O(nsqrt{n}logn))( ext{DFS})(+) (sb) 暴力莫队(时间巨大)

    • (O(nsqrt{n}))( ext{DFS})(+) 回滚莫队(效率一般)

    但就是没找到一篇 ( ext{DFS})(+) 分治,让我来做全网第一篇吧(只是我没有找到,希望不要打脸吧)。

    【分析】

    子树查询转到序列上后其本质是一系列的区间查询,我们知道,要想用分治是需要满足一定单调性的,那么这些区间是否有我们想要的性质呢?

    先给出一些定义:

    • (size(x)) 表示 (|subtree(x)|),即以 (x) 为根的子树大小。

    • (dfn(x)) 表示节点 (x)( ext{DFS}) 序。

    • (idx(i)) 表示 ( ext{DFS})(i) 所对应的节点编号,满足 (dfn(idx(i))=i, idx(dfn(x))=x)

    • (Rdfn(i)) 表示 (i+size(idx(i))-1),即 ( ext{DFS})(i) 所对应的的节点子树中最大的 ( ext{DFS}) 序。

    【引理】

    抛结论
    (i'<i),若存在一个 (j) 满足 (i leqslant j leqslant Rdfn(i))(i' leqslant j leqslant Rdfn(i')),那么 一定有 (Rdfn(i') geqslant Rdfn(i))
    (按照 ( ext{YudeS}) 巨佬所说,穿过 (j) 的区间有互相包含的关系,放在此处即是区间 ([i,Rdfn(i)]) 被包含于 ([i',Rdfn(i')]) 中 )。

    其实很简单,稍想一下就明白了。

    证明
    由给出的两个条件可知 (idx(j) in subtree(idx(i)))(idx(j) in subtree(idx(i'))),所以 (idx(i),idx(i')) 均为(idx(j)) 的祖先节点。
    又因为 (i'<i),且任意一个节点 (x) 的所有祖先都在从根到 (x) 的简单路径上,所以 (idx(i')) 应为 (idx(i)) 的祖先节点,于是有 (Rdfn(i') geqslant Rdfn(i))

    【算法实现】

    回到这道题,有了上面那个性质,用分治已经很显然了吧,对于一层 ((L,mid,R)) 扫一遍计算出:满足 (L leqslant i leqslant mid)(mid+1 leqslant Rdfn(i) leqslant R) 的所有 (ans[i])

    时间复杂度为:(O(nlogn))

    核心操作就一个分治函数,好想又好写,居然没人用....

    像这种 (dot) 的题应该都可以用 ( ext{DFS})(+) 回滚莫队/分治 搞吧,(insert) 函数都不需要改,时间复杂度和分治基本一样(蒟蒻瞎口胡,可信度极低

    【Code】

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #define LL long long
    #define Re register int
    using namespace std;
    const int N=1e5+3;
    int o,n,m,x,y,t,tmp,dfn_O,A[N],Q[N],cnt[N],dfn[N],idx[N],size[N],head[N];LL ans,Ans[N];
    struct QAQ{int to,next;}a[N<<1];
    inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    inline void dfs(Re x,Re fa){//预处理dfn序
        idx[dfn[x]=++dfn_O]=x,size[x]=1;
        for(Re i=head[x],to;i;i=a[i].next)
            if((to=a[i].to)!=fa)dfs(to,x),size[x]+=size[to];
    }
    inline void CL(){//清空贡献,从zero开始
        while(t)cnt[Q[t--]]=0;ans=tmp=0;
    }
    inline void insert(Re x){//加入点x的贡献
        ++cnt[Q[++t]=A[x]];
        if(cnt[A[x]]>tmp)tmp=cnt[ans=A[x]];
        else if(cnt[A[x]]==tmp)ans+=A[x];
    }
    inline void sakura(Re L,Re R){//分治解决(L,R)
        if(L==R){if(size[idx[L]]==1)Ans[idx[L]]=A[idx[L]];return;}
        Re mid=L+R>>1;
        sakura(L,mid),sakura(mid+1,R);//递归解决下面的
        Re p=mid;CL();//搞一个指针p
        for(Re i=mid,j;i>=L&&(j=i+size[idx[i]]-1)<=R;--i){//当j=Rdfn(i)大于R时就可以结束了
            insert(idx[i]);
            if(j<=mid)continue;//只解决对于j>mid的部分
            while(p<j)insert(idx[++p]);//由性质可知满足大于mid的那部分j是单调递增的,不断移动指针p即可
            Ans[idx[i]]=ans;//获得答案
        }
    }
    int main(){
    //  freopen("123.txt","r",stdin);
        in(n),m=n-1;
        for(Re i=1;i<=n;++i)in(A[i]);
        while(m--)in(x),in(y),add(x,y),add(y,x);
        dfs(1,0),sakura(1,n);
        for(Re i=1;i<=n;++i)printf("%lld ",Ans[i]);
    }
    

    【参考资料】

  • 相关阅读:
    批量新增百万条数据 十百万条数据
    sqlserver 组内排序
    EF ++属性会更新实体
    Entity Framework Core: A second operation started on this context before a previous operation completed
    abp Cannot access a disposed object. A common cause of this error is disposing
    abp xunit Can not register IHostingEnvironment. It should be a non-abstract class. If not, it should be registered before.”
    hangfire enqueued but not processing(hangfire 定时任务入队列但不执行)
    EF 更新实体 The instance of entity type 'BabyEvent' cannot be tracked because another instance
    datatable to entiy list 不支持可空类型和枚举类型
    webapi 设置不显示接口到swaggerUI
  • 原文地址:https://www.cnblogs.com/Xing-Ling/p/12336327.html
Copyright © 2011-2022 走看看