CF 375D. Tree and Queries【莫队】

题意:

一棵树,询问一个子树内出现次数≥k≥k的颜色有几种



强制在线见上一道

用莫队不知道比分块高到哪里去了,超好写不用调7倍速度!!!

可以用分块维护出现次数这个权值,实现$O(1)-O(\sqrt{N})$修改查询

#pragma GCC optimize ("O2")
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
const int N=1e5+5, M=245, S=425;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();}
    return x*f;
}

int n,Q,col,a[N],u,v,k;
int cou[N], big[N], tot, mark[N];bool biiig[N];
struct edge{int v,ne;}e[N<<1];
int cnt,h[N];
inline void ins(int u,int v){
    e[++cnt]=(edge){v,h[u]}; h[u]=cnt;
    e[++cnt]=(edge){u,h[v]}; h[v]=cnt;
}
int dfc,L[N],R[N];
int t[N];
void dfs(int u,int fa){
    L[u]=++dfc; a[dfc]=t[u];
    for(int i=h[u];i;i=e[i].ne)
        if(e[i].v!=fa) dfs(e[i].v, u);
    R[u]=dfc;
}

int block,m,pos[N];
struct _blo{int l,r;}b[M];
void ini(){
    //block=sqrt(n);
    block=420;
    m=(n-1)/block+1;
    for(int i=1;i<=n;i++) pos[i]=(i-1)/block+1;
    for(int i=1;i<=m;i++) b[i].l=(i-1)*block+1, b[i].r=i*block;
    b[m].r=n;
}

struct Block{
    int f[M][M][S], c[N], s[M][N];

    void Set0(int x){
        for(int i=1;i<=col;i++) s[x][i]=s[x-1][i];
        for(int i=b[x].l; i<=b[x].r; i++) s[x][a[i]]++;
    }

    void Set1(int x){
        for(int t=x;t<=m;t++){
            for(int i=b[t].l; i<=b[t].r; i++) if(!biiig[ a[i] ]) c[a[i]]++;
            for(int i=b[t].l; i<=b[t].r; i++) if(!biiig[ a[i] ] && c[a[i]]>0){
                int _=s[t-1][a[i]] - s[x-1][a[i]];
                f[x][t][ _+c[a[i]] ]++;
                f[x][t][ _ ]--;
                c[a[i]]=0;
            }
            for(int i=block; i>=1; i--) f[x][t][i]+=f[x][t][i+1];
            for(int i=1; i<=block; i++) f[x][t][i]+=f[x][t-1][i];
        }
    }

    int Que(int l,int r,int k){
        int pl=pos[l], pr=pos[r];
        int ans=0;
        if(pl==pr){
            for(int i=l; i<=r; i++) c[a[i]]++;
            for(int i=l; i<=r; i++) if(c[a[i]]>0) ans+= c[a[i]]>=k, c[a[i]]=0;
        }else{
            for(int i=1; i<=tot; i++) mark[ big[i] ]=0;
            vector<int> v;
            int *rr=s[pr], *ll=s[pl-1];
            for(int i=l; i<=b[pl].r; i++){
                mark[ a[i] ]=1;
                if(rr[a[i]] - ll[a[i]]>=k)
                    c[a[i]]++, v.push_back(a[i]);
            }
            for(int i=b[pr].l; i<=r; i++){
                mark[ a[i] ]=1;
                if(rr[a[i]] - ll[a[i]]>=k)
                    c[a[i]]++, v.push_back(a[i]);
            }

            for(int i=0; i<(int)v.size(); i++) if(c[v[i]]>0){
                int _=s[pr-1][v[i]] - s[pl][v[i]];
                if(biiig[ v[i] ]) ans+= _+c[v[i]]>=k;
                else ans+= (_<k && _+c[v[i]]>=k);
                c[v[i]]=0;
            }

            if(k<=block) ans+=f[pl+1][pr-1][k];
            for(int i=1;i<=tot;i++) if(!mark[ big[i] ])
                ans+= s[pr-1][big[i]] - s[pl][big[i]] >= k;
        }
        return ans;
    }
}B;

int main(){
//    freopen("in","r",stdin);
    n=read(); Q=read(); ini();
    for(int i=1;i<=n;i++) a[i]=t[i]=read(), col=max(col, a[i]), cou[a[i]]++;
    for(int i=1;i<n;i++) ins(read(), read());
    dfs(1,0);

    for(int i=1;i<=col;i++) if(cou[i]>block) big[++tot]=i, biiig[i]=1;
    for(int i=1;i<=m;i++) B.Set0(i);
    for(int i=1;i<=m;i++) B.Set1(i);

    while(Q--){
        u=read(); k=read();
        if(k>n) puts("0");
        else printf("%d\n", B.Que(L[u], R[u], k) );
    }
}
时间: 2024-08-10 17:19:52

CF 375D. Tree and Queries【莫队】的相关文章

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

[Codeforces375D]Tree and Queries(莫队算法)

题意:给定一棵树,每个节点有颜色,对于每个询问(u,k)询问以u为根节点的子树下有多少种颜色出现次数>=k 因为是子树,跟dfs序有关,转化为一段区间,可以用莫队算法求解 直接用一个数组统计出现次数>=k的颜色 Code #include <cstdio> #include <algorithm> #include <cmath> #define N 100010 using namespace std; int n,m,A[N],bl[N],Ans[N],

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>

SPOJ.COT2 Count on a tree II(树上莫队)

题目链接(同上一题苹果树) 为什么第10个点T了一晚上.. 下面那个却AC了?跑的也不慢. TLE: /* 在DFS序做莫队 当一个点不是另一个点的LCA时,需要加上它们LCA的贡献 */ #include <cmath> #include <cstdio> #include <cctype> #include <algorithm> #define gc() getchar() //#define gc() (SS==TT&&(TT=(SS

Gym101138D Strange Queries 莫队、前缀和、容斥

传送门 THUWC2019D1T1撞题可还行 以前有些人做过还问过我,但是我没有珍惜,直到进入考场才追悔莫及-- 设\(que_{i,j}\)表示询问\((1,i,1,j)\)的答案,那么询问\((a,b,c,d)=que_{b,d} - que_{a-1 , d} - que_{b , c - 1} + que_{a - 1 , c - 1}\) 把一个询问拆成\(4\)个询问,然后对这\(4\)个询问莫队就可以了 不知道怎么回事THUWC上想到了莫队想到了前缀和想到了容斥就是没想到莫队+前缀

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

SP10707 COT2 - Count on a tree II (树上莫队)

参考博客 对于树上的路径询问问题 O(1)的时间加入或删除一个点的贡献 -> \(O(n\sqrt n)\)的复杂度求出所有询问的答案 对树上的结点进行分块,离线询问后排序,顺序遍历暴力转移路径(转移时加入或删除路径上的点的贡献即可). 关于转移路径:首先定义路径:设\(T_u\)为\(u\) 到根的路径上边的集合,那么\(u\)到\(v\) 的路径上的边的集合就是\(T_u \triangle T_v\) (\(\triangle\) 是对称差).要从\(u\rightarrow v\) 转移

CodeForces 375D. Tree and Queries【树上启发式合并】

传送门 题意 给出一棵 \(n\) 个结点的树,每个结点有一个颜色 \(c_i\) . 询问 \(q\) 次,每次询问以 \(v\) 结点为根的子树中,出现次数 \(\ge k\) 的颜色有多少种.树的根节点是 \(1\). 题解 反正我看见这个 \(\ge k\) 就觉得要用线段树,实际上好像不用写线段树的 Orz. 还是树上启发式合并,记录每种颜色出现的次数,然后线段树记录某种次数有多少颜色,更改就在线段树上改. 这是最后一道树上启发式合并的例题了,以后遇到再刷. #include <bit

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]