hdu4358 树形转线性 线段树

题意:给定了一棵树和树上每一个节点的权值 然后询问是给出子树顶点求子树中出现k次的节点权值的个数

解法:把树形先转化成线性的 然后离线所有询问 不要忘了把树上节点的权值离散化 弄完之后就直接往

扫就可以了  当某一种权值>=k时才进行维护

#pragma comment(linker,"/STACK:102400000,102400000")
#include<cstdio>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid ((l+r)>>1)
#define maxn 111111
vector<int>g[maxn];
int L[maxn],R[maxn],tot,val[maxn],num[maxn],head[maxn],vis[maxn],n;
void dfs(int u,int f){
    L[u]=++tot;
    num[tot]=val[u];
    for(int i=0;i<g[u].size();++i){
        int v=g[u][i];
        if(v==f)continue;
        dfs(v,u);
    }
    R[u]=tot;
}
struct node{
    int v,l,r,id;
}_q[maxn];
int cmp(node x,node y){return x.r<y.r;}
int add[maxn<<2];
inline void down(int rt){
    if(add[rt]){
        add[ls]+=add[rt],add[rs]+=add[rt];
        add[rt]=0;
    }
}
inline void build(int rt,int l,int r){
    add[rt]=0;if(l==r)return ;
    build(ls,l,mid);
    build(rs,mid+1,r);
}
inline void ins(int rt,int l,int r,int L,int R,int w){
    if(L<=l&&r<=R){add[rt]+=w;return ;}
    down(rt);
    if(L<=mid)ins(ls,l,mid,L,R,w);
    if(mid<R)ins(rs,mid+1,r,L,R,w);
}
inline int query(int rt,int l,int r,int pos){
    if(l==r)return add[rt];
    down(rt);
    if(pos<=mid)return query(ls,l,mid,pos);
    return query(rs,mid+1,r,pos);
}
int x[maxn];
vector<int>pos[maxn];
int ans[maxn];
int main()  {
    int t,_=0,k,u,v,q;
    scanf("%d",&t);
    while(t--) {
        scanf("%d%d",&n,&k);
        tot=0;
        rep(i,1,n){
            scanf("%d",&val[i]),x[i]=val[i];
            g[i].clear(),pos[i].clear();
        }
        rep(i,1,n-1){
            scanf("%d%d",&u,&v);
            g[u].push_back(v),g[v].push_back(u);
        }
        sort(x+1,x+1+n);
        
        int  cnt=(int)(unique(x+1,x+n+1)-x-1);
        for(int i=1;i<=n;i++)val[i]=(int)(lower_bound(x+1,x+cnt+1,val[i])-x);
        
        dfs(1,-1);
        
        scanf("%d",&q);
        rep(i,0,q-1){
            scanf("%d",&_q[i].v);
            _q[i].l=L[_q[i].v];_q[i].r=R[_q[i].v];
            _q[i].id=i;
        }

        sort(_q,_q+q,cmp);build(1,1,tot);
        int cur=0;
        rep(i,1,tot){
            int va=num[i];
            pos[va].push_back(i);
            int sz=(int)pos[va].size();
            if(sz>=k){
                if(sz==k)ins(1,1,tot,1,pos[va][sz-k],1);
                else{
                    ins(1,1,tot,1,pos[va][sz-k-1],-1);
                    ins(1,1,tot,pos[va][sz-k-1]+1,pos[va][sz-k],1);
                }
            }
            while(cur<q&&_q[cur].r==i){
                ans[_q[cur].id]=query(1,1,tot,_q[cur].l);
                ++cur;
            }
        }
        if(_)puts("");
        printf("Case #%d:\n",++_);
        rep(i,0,q-1)printf("%d\n",ans[i]);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值