zoukankan      html  css  js  c++  java
  • 树上的颜色

    【问题描述】
    给出一棵有N个点的有根树,结点编号为1,2,3,……,N,根结点编号为1,编号为i的结点图上颜色Ci。现在有M个询问,每个询问要求求出以结点u为根的子树上涂有此种颜色的结点个数不小于k的颜色个数有多少。

    【输入格式】
    输入文件名为color.in。
    第一行包含两个正整数N和M。
    第二行包含N个正整数,C1,C2,…,CN。
    接下来的N-1行每行有两个正整数x和y,表示结点x和y有边相连。
    再接下来的M行每行有两个正整数u和k,表示一个询问。

    【输出格式】
    输出文件名为color.out。
    输出M行,每行一个非负整数,对应每个询问的答案。

    【输入输出样例1】
    color.in
    4 1
    1 2 3 4
    1 2
    2 3
    3 4
    1 1
    color.out
    4

    【输入输出样例2】
    color.in
    8 5
    1 2 2 3 3 2 3 3
    1 2
    1 5
    2 3
    2 4
    5 6
    5 7
    5 8
    1 2
    1 3
    1 4
    2 3
    5 3
    color.out
    2
    2
    1
    0
    1

    【数据规模与约定】
    对于10%的数据,N≤100,M≤100,Ci≤100;
    对于30%的数据,N≤500,M≤100;
    对于60%的数据,N≤2000,M≤100000;
    对于100%的数据,N≤100000,M≤100000,Ci≤100000。

    【题解】
    10分做法:
    直接暴力,O(N*N*M)

    30分做法:
    对颜色离散化后直接暴力。

    60分做法:
    离线处理询问,将结点相同的询问放在一起处理。对一个结点,暴力处理子树上所有点,用color[i]统计第i钟颜色的数量,用sum[i]统计颜色数量为i,i+1,i+2,……的颜色个数。设要加入一个点,其颜色为x,则sum[color[x]+1]增加1,sum的其他值不变,同时color[x]++即可。同理,删去一个点,sum[color[x]–]–即可。因此处理一棵子树只需O(n)的时间统计出color和sum数组,并且做完后清空color和sum数组也是O(n)的,而sum数组就是询问的答案。时间复杂度O(N²)

    100分做法:
    在60分做法的基础上,考虑处理点u的时候,假设其孩子结点为v1,v2,…,vk,按顺序先处理其孩子结点,每做完一个需要清空,发现vk做完后不需要清空,因此不妨将孩子结点的子树结点个数最多的放在最后处理,这样可以证明时间复杂度变为O(NlogN)。(假设时间为2NlogN,然后用数学归纳法)

    这已经是我第n次树上题爆0了,我还是应该总结一下经验教训,克服对树上题的恐惧感,加强对该类题目的训练,先试着打对暴力,再逐渐像正解靠拢。同时记住,树上题有两个很套路的部分:链式前向星的存储和从父亲节点搜儿子的DFS。
    存储:

    struct edge
    {
        int to,next;
    }a[MAX<<1];
    void Add(int u,int v)
    {
        a[++cnt]=(edge){v,head[u]};
        head[u]=cnt;
    }

    DFS:

    void dfs(ll u,ll fa)
    {
        for(ll i=head[u];i;i=a[i].next)
        {
            ll v=a[i].to;
            if(v==fa) continue;
            dfs(v,u);
            //f[u]=max(f[u],f[v]+a[i].w);
        }
        //for(ll i=head[u];i;i=a[i].next)
         //   if(fa!=a[i].to)
           //     ans+=(f[u]-f[a[i].to]-a[i].w);
    }

    据学长的说法,这道题有两种做法:平衡树(Splay)和树上莫队。我自己只学会了莫队。
    莫队:按我的理解,莫队就是一种分块(优雅的暴力)。他把一个区间分作sqrt(n)+1个块(当n为平方数时是sqrt(n))。再把分成的块以,左端点所在块的标号为第一优先级,右端点为第二优先级排序。然后询问的答案都由上一个询问更新而来。
    树上莫队:讲究的是把子树变为链,在先序遍历前提下,左端点为子树根节点,右端点为搜到的最后一个儿子。把他们存下来,再按普通莫队搞即可。

    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #define fp(i,a,b) for(int i=a;i<=b;i++)
    #define fq(i,a,b) for(int i=a;i>=b;i--)
    #define il inline
    #define ll long long 
    using namespace std;
    const int MAX=100005;
    struct edge
    {
        int to,next;
    }a[MAX<<1];
    int head[MAX],cnt;
    struct query
    {
        int l,r,k,id;
    }q[MAX];
    int line[MAX],Begin[MAX],End[MAX],tot;//line是最终处理出来的数列 Begin End表示某一个节点对应的某个区间
    int n,m,gen,re[MAX];
    int col[MAX],num[MAX],sum[MAX];
    int ans[MAX];
    void Add(int u,int v)
    {
        a[++cnt]=(edge){v,head[u]};
        head[u]=cnt;
    }
    void dfs(int u,int fa)
    {
        Begin[u]=++tot;
        line[tot]=col[u];
        for (int e=head[u];e;e=a[e].next)
        {
            int v=a[e].to;
            if (v==fa) continue;
            dfs(v,u);
        }
        End[u]=tot;
    }
    bool cmp(query a,query b)
    {
        return (re[a.l]<re[b.l])||(re[a.l]==re[b.l]&&a.r<b.r);
    }
    il int gi()
    {
       int x=0;
       short int t=1;
       char ch=getchar();
      while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    int main()
    {
        n=gi();m=gi();gen=sqrt(n);
        fp(i,1,n)
            col[i]=gi();
        fp(i,1,n)
            re[i]=(i-1)/gen+1;
        fp(i,1,n-1)
        {
            int x=gi(),y=gi();
            Add(x,y);Add(y,x);
        }
        dfs(1,0);
        fp(i,1,m)
        {
            int u=gi();
            q[i]=(query){Begin[u],End[u],gi(),i};
        }
        sort(q+1,q+1+m,cmp);
        int L=1,R=0;
        fp(i,1,m)//60分做法的暴力搞
        {
            while(q[i].l<L) L--,num[line[L]]++,sum[num[line[L]]]++;
            while(q[i].l>L) sum[num[line[L]]]--,num[line[L]]--,L++;
            while(q[i].r>R) R++,num[line[R]]++,sum[num[line[R]]]++;
            while(q[i].r<R) sum[num[line[R]]]--,num[line[R]]--,R--;
            ans[q[i].id]=sum[q[i].k];
        }
        fp(i,1,m)
            printf("%d
    ",ans[i]);
        return 0;
    }
    
  • 相关阅读:
    python 执行sql得到字典格式数据
    python爬虫 url链接编码成gbk2312格式
    windows环境下elasticsearch安装教程(单节点)
    python SQLServer 存储图片
    爬虫的本质是和分布式爬虫的关系
    requests form data 请求 爬虫
    mysql 删除 binlog 日志文件
    查看mysql数据表的大小
    xshell 连接报错 Disconnected from remote host
    centos 7.3 安装 mysqldb 报错 EnvironmentError: mysql_config not found ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.
  • 原文地址:https://www.cnblogs.com/yanshannan/p/7392300.html
Copyright © 2011-2022 走看看