Codeforces 375D D. Tree and Queries

传送门

题意:

给一棵树,每个节点有一个颜色,询问x为根的子树,出现次数大于等于k的颜色个数。

输入格式:

第一行 2 个数 n,m 表示节点数和询问数。

接下来一行 n 个数,第 i 个数 ci ?表示节点 i 的颜色。

接下来 n-1 行,每行两个数 a,b 表示一条边。

接下来 m 行,每行两个数 x,k 表示一组询问。

数据范围: $n.m,c,k \in [1,10^5]$

显然可以 $dsu\ on\ tree$

可以用权值树状数组直接维护当前每个出现次数大于等于 $k$ 的颜色数量

但是因为每次修改都只有加 $1$ 或减 $1$

所以可以直接维护 $ans[i]$ 表示出现次数大于等于 $i$ 的颜色数量

具体代码就可以这样维护:

//cnt是每种颜色当前出现次数
inline void ins(int c) { cnt[c]++; ans[cnt[c]]++; }
inline void del(int c) { ans[cnt[c]]--; cnt[c]--; }

其他就是 $dsu\ on\ tree$ 的基本操作了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<‘0‘||ch>‘9‘) { if(ch==‘-‘) f=-1; ch=getchar(); }
    while(ch>=‘0‘&&ch<=‘9‘) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=2e6+7;
int fir[N],from[N<<1],to[N<<1],cntt;
inline void add(int &a,int &b) { from[++cntt]=fir[a]; fir[a]=cntt; to[cntt]=b; }
int n,m,col[N],Ans[N];
struct dat{
    int x,k,id;//以节点x为根,出现次数大于等于k的询问,编号为id
    inline bool operator < (const dat &tmp) const {
        return x<tmp.x;
    }
}d[N];//存询问并准备把询问按x排序
int sz[N],son[N];
void dfs1(int x,int fa)
{
    sz[x]=1;
    for(int i=fir[x];i;i=from[i])
    {
        int &v=to[i]; if(v==fa) continue;
        dfs1(v,x); sz[x]+=sz[v];
        if(sz[v]>sz[son[x]]) son[x]=v;
    }
}
int id[N],dfs_clock,cnt[N],ans[N];//id是dfs序为i的节点编号
inline void ins(int c) { cnt[c]++; ans[cnt[c]]++; }
inline void del(int c) { ans[cnt[c]]--; cnt[c]--; }
inline void work(int x)//处理与节点x有关的询问
{
    for(int i=lower_bound(d+1,d+m+1,(dat){x,0,0})-d ; d[i].x==x ; i++)
        Ans[d[i].id]=ans[d[i].k];
}
void dfs2(int x,int fa,bool flag)//flag判断是否要清空子树
{
    id[++dfs_clock]=x; int L=dfs_clock;//L是轻儿子子树左区间
    if(!son[x]) { ins(col[x]); work(x); if(!flag) del(col[x]); return; }
    //注意上面对于叶节点的特判中要先ins再work再del
    for(int i=fir[x];i;i=from[i])
    {
        int &v=to[i]; if(v==fa||v==son[x]) continue;
        dfs2(v,x,0);
    }
    int R=dfs_clock;//轻儿子子树右区间
    dfs2(son[x],x,1);
    for(int i=L;i<=R;i++) ins( col[id[i]] );
    work(x);
    if(!flag) for(int i=L;i<=dfs_clock;i++) del( col[id[i]] );//注意清除的区间不是[L,R]
}
int main()
{
    n=read(),m=read(); int a,b;
    for(int i=1;i<=n;i++) col[i]=read();
    for(int i=1;i<n;i++)
    {
        a=read(),b=read();
        add(a,b); add(b,a);
    }
    dfs1(1,0);
    for(int i=1;i<=m;i++) d[i].x=read(),d[i].k=read(),d[i].id=i;
    sort(d+1,d+m+1);//离散化
    dfs2(1,0,1);
    for(int i=1;i<=m;i++) printf("%d\n",Ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/LLTYYC/p/10961360.html

时间: 2024-12-20 18:25:13

Codeforces 375D D. Tree and Queries的相关文章

Codeforces 375D Tree and Queries(DFS序+莫队+树状数组)

题目链接  Tree and Queries 题目大意  给出一棵树和每个节点的颜色.每次询问vj, kj 你需要回答在以vj为根的子树中满足条件的的颜色数目, 条件:具有该颜色的节点数量至少为kj. (莫队居然可以过) 首先转DFS序,这样就变成了区间查询. 然后直接套用莫队,求出每次询问状态下的t[],t[k]表示当前区间内拥有k个节点的颜色数量. 然后统计t[k] + t[k + 1], ..., t[MAX]即可,这个过程用树状数组维护. #include <bits/stdc++.h>

CodeForces 375D Tree and Queries 莫队||DFS序

Tree and Queries 题意:有一颗以1号节点为根的树,每一个节点有一个自己的颜色,求出节点v的子数上颜色出现次数>=k的颜色种类. 题解:使用莫队处理这个问题,将树转变成DFS序区间,然后就是开一个数据记录下出现次数为k次的颜色数目,查询的时候直接返回这个数组中的对应的值就行了. 注意的就是记得将节点的颜色传递给dfs序对应位置的颜色. 这个忘记了找了好久的bug. 代码: 1 #include<bits/stdc++.h> 2 using namespace std; 3

codeforces 570 D. Tree Requests 树状数组+dfs搜索序

链接:http://codeforces.com/problemset/problem/570/D D. Tree Requests time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Roman planted a tree consisting of n vertices. Each vertex contains a low

Educational Codeforces Round 23 F. MEX Queries(线段树)

题目链接:Educational Codeforces Round 23 F. MEX Queries 题意: 一共有n个操作. 1.  将[l,r]区间的数标记为1. 2.  将[l,r]区间的数标记为0. 3.  将[l,r]区间取反. 对每个操作,输出标记为0的最小正整数. 题解: hash后,用线段树xjb标记一下就行了. 1 #include<bits/stdc++.h> 2 #define ls l,m,rt<<1 3 #define rs m+1,r,rt<&l

codeforces 570 D Tree Requests

题意:给出一棵树.每一个结点都有一个字母,有非常多次询问,每次询问.以结点v为根的子树中高度为h的后代是否可以经过调整变成一个回文串. 做法: 推断能否够构成一个回文串的话,仅仅须要知道是否有大于一个的奇数数目的字母就可以.为了非常快的訪问到一个区间.记录前缀和就可以.为了省内存,状压奇偶就可以. 为了非常快的找到以结点v为根的子树中高度为h的后代,须要dfs整棵树.然后记录每一个结点第一次訪问它的时间戳以及离开它的时间戳,就能够二分出来. 为了省内存,能够离线处理询问. #include<ma

Problem - D - Codeforces Fix a Tree

Problem - D - Codeforces  Fix a Tree 看完第一名的代码,顿然醒悟... 我可以把所有单独的点全部当成线,那么只有线和环. 如果全是线的话,直接线的条数-1,便是操作数. 如果有环和线,环被打开的同时,接入到线上.那就是线和环的总数-1. 如果只有环的话,把所有的环打开,互相接入,共需n次操作. #include <cstdio> #include <algorithm> using namespace std; const int maxn =

CodeForces - 383C Propagating tree(dfs + 线段树)

题目大意: 给出一棵树,树上每个节点都有权值,然后有两个操作. 1 x val 在结点x上加上一个值val,x的儿子加上 -val,x的儿子的儿子加上 - (-val),以此类推. 2 x 问x节点的值. 思路分析: 每个节点上加值都是给自己的儿子节点加,而且这个是颗树. 比如样例上的,如果你给node 1加一个值,那么五个节点都加. 再给node 2加个值,2的儿子节点也加了,之前给1加的值也要加到2号节点的儿子. 所以你会发现节点的儿子会存在一个从属的关系. 这样的话,我们可以把所有节点从新

Tree and Queries CodeForces - 375D 树上莫队

http://codeforces.com/problemset/problem/375/D 树莫队就是把树用dfs序变成线性的数组. (原数组要根据dfs的顺序来变化) 然后和莫队一样的区间询问. 这题和普通莫队有点区别,他需要的不单单是统计区间元素种类个数,是区间元素种类个数 >= k[i]的个数. 考虑最简单的用bit维护,复杂度多了个log 观察到每次只需要 + 1  或者 -1 用一个数组sum[k]表示种类数大于等于k的ans 在numc[val]++之后,sum[numc[val]

codeforces 375D . Tree and Queries 启发式合并 || dfs序+莫队

题目链接 一个n个节点的树, 每一个节点有一个颜色, 1是根节点. m个询问, 每个询问给出u, k. 输出u的子树中出现次数大于等于k的颜色的数量. 启发式合并, 先将输入读进来, 然后dfs完一个节点就处理跟它有关的询问. 感觉不是很难, 然而.....WA了n次最后还是看的别人的代码 1 #include <iostream> 2 #include <vector> 3 #include <cstdio> 4 #include <cstring> 5