codeforces 375D Tree and Queries

题目链接

题目大意

给定一棵树,n个节点,m个询问。
每次询问以 vi 为根的子树中,点数大于等于 ki 的颜色个数。

题解

对于这种询问子树的问题,经常会转到序列上。毕竟,序列较树有着无比优越性。
比如说,莫队算法。
利用dfs序将树转到序列上后,就可以套用莫队算法了。
维护区间时需要维护每种颜色的个数 cnti ,同时要维护颜色大于等于i的颜色个数 sumi
每次删除一个数,就把这种颜色的cnt减一,由于只有这种颜色变化了,所以sum变化的只有原来的 cntcoli ,所以在减一之前,先把 sumcntcoli 减一。
当加入一个数时,先把这个颜色的cnt加一,然后, sumcntcoli 也要加一。

#include<cstdio>
#include<vector>
#include<cassert>
#include<cmath>
#include<cctype>
#include<set>
#include<algorithm>
using namespace std;
const int M=100005;
int col[M],L[M],R[M],dfs_clock,s;
int ans[M],cnt[M],sum[M],tmp[M];
vector<int>edge[M];
struct Query{
    int L,R,num,id;
}q[M];
inline void rd(int&res){
    res=0;char c;
    while(c=getchar(),!isdigit(c));
    do res=res*10+(c^48);
    while(c=getchar(),isdigit(c));
}
void rec(int x,int f){
    L[x]=++dfs_clock;
    for(int i=0;i<edge[x].size();i++)
        if(edge[x][i]!=f) rec(edge[x][i],x);
    R[x]=dfs_clock;
}
bool cmp(Query a,Query b){
    if(a.L/s==b.L/s) return a.R<b.R;
    return a.L/s<b.L/s;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    s=(int)sqrt(n);
    for(int i=1;i<=n;i++)
        rd(col[i]);
    for(int i=1;i<n;i++){
        int a,b;
        rd(a);rd(b);
        edge[a].push_back(b);
        edge[b].push_back(a);
    }
    rec(1,0);
    for(int i=0;i<m;i++){
        int a,b;
        rd(a);rd(b);
        q[i]=(Query){L[a],R[a],b,i};
    }
    for(int i=1;i<=n;i++)
        tmp[i]=col[i];
    for(int i=1;i<=n;i++)
        col[L[i]]=tmp[i];
    sort(q,q+m,cmp);
    int L=0,R=0,res=0;
    for(int i=0;i<m;i++){
        while(R<q[i].R){
            R++;
            cnt[col[R]]++;
            sum[cnt[col[R]]]++;
        }
        while(L>q[i].L){
            L--;
            cnt[col[L]]++;
            sum[cnt[col[L]]]++;
        }
        while(L<q[i].L){
            sum[cnt[col[L]]]--;
            cnt[col[L]]--;
            L++;
        }
        while(R>q[i].R){
            sum[cnt[col[R]]]--;
            cnt[col[R]]--;
            R--;
        }
        ans[q[i].id]=sum[q[i].num];
    }
    for(int i=0;i<m;i++)
        printf("%d\n",ans[i]);
    return 0;
}

遗留问题

这题还可以用map的启发式合并来写。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值