zoukankan      html  css  js  c++  java
  • [Codeforces Round #221 (Div. 1)][D. Tree and Queries]

    题目链接:375D - Tree and Queries

    题目大意:给你一个有n个点的树,每个点都有其对应的颜色,给出m次询问(v,k),问v的子树中有多少种颜色至少出现k次

    题解:先对所有的询问进行分类,即对所有相同的v合并到一起,这样就能转为离线处理(更新每个点的状态时同时求出答案)

       开两个map<int,int>,cnt[i][j]表示 i 节点的子树中 j 颜色出现了多少次,f[i][j]则表示 i 节点的子树中至少出现 j 次的颜色个数。dfs的时候暴力枚举所有颜色合并能够得出正确答案,但这样做显然会TLE,因此需要用到启发式合并进行优化。即每次合并的时候是将小的集合并到大的集合中去,这样做的话就只需要枚举小集合里的元素,可以将合并的时间复杂度缩减到O(log n)的级别

    #include<bits/stdc++.h>
    using namespace std;
    #define N 100001
    int n,m,u,v,c[N],k[N],ans[N];
    map<int,int>cnt[N],f[N];
    vector<int>d[N],q[N];
    void dfs(int cur,int pre)
    {
        cnt[cur][c[cur]]++,f[cur][1]++;
        for(auto nxt:d[cur])if(nxt!=pre)
          {
          dfs(nxt,cur);
          if(cnt[nxt].size()>cnt[cur].size())
            cnt[cur].swap(cnt[nxt]),f[cur].swap(f[nxt]);
          for(auto x:cnt[nxt])
            {
            int y=cnt[cur][x.first];
            cnt[cur][x.first]+=x.second;
            for(int i=y+1;i<=y+x.second;i++)f[cur][i]++;
            }
          }
        for(auto i:q[cur])ans[i]=f[cur][k[i]];
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
          scanf("%d",&c[i]);
        for(int i=2;i<=n;i++)
          {
          scanf("%d%d",&u,&v);
          d[u].push_back(v);
          d[v].push_back(u);
          }
        for(int i=1;i<=m;i++)
          scanf("%d%d",&v,&k[i]),q[v].push_back(i);
        dfs(1,0);
        for(int i=1;i<=m;i++)
          printf("%d
    ",ans[i]);
    }
    View Code
  • 相关阅读:
    一秒 解决 ERROR 1044 (42000): Access denied for user ''@'localhost' to database 'mysql 问题
    30分钟让你学会 Spring事务管理属性
    判断是否是回文
    Linux自动获取IP地址 重启不会消失
    .The server quit without updating PID file (/var/lib/mysql/pc.pid).
    线程的状态
    linux vim基本操作
    C++ 函数重载和参数的缺省值
    C++ 类中的3种访问权限和继承方式
    C++ 内存管理
  • 原文地址:https://www.cnblogs.com/DeaphetS/p/9573428.html
Copyright © 2011-2022 走看看