zoukankan      html  css  js  c++  java
  • 【CodeForces】600 E. Lomsat gelral (dsu on tree)

    【题目】E. Lomsat gelral

    【题意】给定n个点的树,1为根,每个点有一种颜色ci,一种颜色占领一棵子树当且仅当子树内没有颜色的出现次数超过它,求n个答案——每棵子树的占领颜色的编号和Σci(一棵子树可能有多种占领颜色)。1<=n,ci<=10^5。

    【算法】dsu on tree

    【题解】入门题,讲一下dsu on tree算法。

    一、dsu on tree的适用范围:①子树询问,②支持数组上的快速信息加,③不带修。(注意不需要支持信息删除,只需要能清空信息)

    如果写暴力的时候,每个点需要开一个O(n)的数组,然后要从下往上的合并数组,那么dsu on tree就可以帮你把复杂度降低到O(n log n)。

    二、dsu on tree的过程

    ①递归处理轻儿子,不保存信息。

    ②递归处理重儿子,保存信息。

    ③暴力加入该子树处理重儿子子树外的所有点的信息,得到该子树的答案。

    ④如果父边是轻边,扫描整棵子树清空信息。(操作必须和点数相关才能保证复杂度)

    三、dsu on tree的复杂度证明

    算法思想是先处理轻儿子并且不保存,然后处理重儿子并且保存信息,这样每个点暴力扫描子树都不会包含重儿子子树

    轻重链剖分的特点是每个点到根至多经过log n条轻边,所以一般只要支持快速处理重链。(这个证明可以考虑最左端的底层节点,每跳一次轻边其右儿子都必须翻倍)

    dsu on tree中每个点只会在其到根的路径中的所有轻边被遍历到1次,所以每个点的复杂度是O(log n)的。

    总复杂度O(n log n)。

    对于本题,需要支持快速信息加,记a[i]表示颜色i的出现次数,mx表示最多出现次数,sum表示答案,加的时候更新一下,清空的时候直接mx=sum=0即可。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=100010;
    int c[maxn],first[maxn],tot,sz[maxn],w[maxn],a[maxn],n,mx;
    long long ans[maxn],sum;
    bool vis[maxn];
    struct edge{int v,from;}e[maxn*2];
    void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
    void predfs(int x,int fa){
        sz[x]=1;
        for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){
            predfs(e[i].v,x);
            sz[x]+=sz[e[i].v];
            if(sz[e[i].v]>sz[w[x]])w[x]=e[i].v; 
        }
    }
    void add(int x,int fa,int k){
        a[c[x]]+=k;
        if(k>0){
            if(a[c[x]]>mx){
                mx=a[c[x]];sum=c[x];
            }
            else if(a[c[x]]==mx)sum+=c[x];
        }
        for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa&&!vis[e[i].v])add(e[i].v,x,k);
    }
    void dfs(int x,int fa,int k){
        for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa&&e[i].v!=w[x])dfs(e[i].v,x,0);
        if(w[x])dfs(w[x],x,1),vis[w[x]]=1;
        add(x,fa,1);
        if(w[x])vis[w[x]]=0;
        ans[x]=sum;
        if(!k)add(x,fa,-1),mx=sum=0;
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&c[i]);
        for(int i=1;i<n;i++){
            int u,v;scanf("%d%d",&u,&v);
            insert(u,v);insert(v,u);
        }
        predfs(1,0);dfs(1,0,1);
        for(int i=1;i<=n;i++)printf("%lld ",ans[i]);
        return 0;
    }
    View Code
  • 相关阅读:
    【CTF WEB】函数绕过
    【CTF WEB】命令执行
    【CTF WEB】文件包含
    【CTF WEB】GCTF-2017读文件
    【技巧总结】Penetration Test Engineer[5]-Operating System Security(SQL Server、MySQL提权)
    【技巧总结】Penetration Test Engineer[4]-Web-Security(文件处理、会话管理、访问控制、SSRF、反序列化漏洞)
    【技巧总结】Penetration Test Engineer[3]-Web-Security(SQL注入、XXS、代码注入、命令执行、变量覆盖、XSS)
    【技巧总结】Penetration Test Engineer[2]-Information gathering
    【技巧总结】Penetration Test Engineer[1]-Basic
    【 Linux 】单台服务器上并发TCP连接数
  • 原文地址:https://www.cnblogs.com/onioncyc/p/8906552.html
Copyright © 2011-2022 走看看