HDU 4358 Boring counting (树状数组)

Boring counting

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 98304/98304 K (Java/Others)
Total Submission(s): 1584    Accepted Submission(s): 440


Problem Description
In this problem we consider a rooted tree with N vertices. The vertices are numbered from 1 to N, and vertex 1 represents the root. There are integer weights on each vectice. Your task is to answer a list of queries, for each query, please tell us among all the vertices in the subtree rooted at vertice u, how many different kinds of weights appear exactly K times?
 

Input
The first line of the input contains an integer T( T<= 5 ), indicating the number of test cases.
For each test case, the first line contains two integers N and K, as described above. ( 1<= N <= 10 5, 1 <= K <= N )
Then come N integers in the second line, they are the weights of vertice 1 to N. ( 0 <= weight <= 10 9 )
For next N-1 lines, each line contains two vertices u and v, which is connected in the tree.
Next line is a integer Q, representing the number of queries. (1 <= Q <= 10 5)
For next Q lines, each with an integer u, as the root of the subtree described above.
 

Output
For each test case, output "Case #X:" first, X is the test number. Then output Q lines, each with a number -- the answer to each query.

Seperate each test case with an empty line.
 

Sample Input
  
  
1 3 1 1 2 2 1 2 1 3 3 2 1 3
 

Sample Output
  
  
Case #1: 1 1 1
 

Author
fish@UESTC_Oblivion
 

Source
 

Recommend
zhuyuanchen520
 
思路:将一棵树转化成区间,可以用时间戳,查询一个节点就可以表示成一个区间。然后这题就可以变成求一个区间内出现过K次的数字的个数。这里可以树状数组来维护一段"时间区间"中出现k次的数字的个数。利用离线,对每次查询的区间按右区间从小到大排序。按时间遍历,每次将当前时间的u值的位置插入到pos[u]的容器。之后每次更新的时候,都要将之前的区间删除,所以需要维护u值出现的位置。sum(q[i].l)就是当前查询的结果。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int maxn=100005;
int c[maxn],index,n;
int head[maxn],edge,begin[maxn],end[maxn];
int w[maxn],ans[maxn],value[maxn];
int to[maxn<<1],next[maxn<<1];
bool vis[maxn];
vector<int>pos[maxn];
map<int,int>wflag;

struct node
{
    int l,r,id;
} q[maxn];

bool cmp(node a,node b)
{
    return a.r<b.r;
}

inline void addedge(int u,int v)
{
    to[edge]=v,next[edge]=head[u],head[u]=edge++;
}

void init()
{
    wflag.clear();
    memset(c,0,sizeof(c));
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    edge=index=0;
}

inline int lowbit(int x)
{
    return x&(-x);
}

void add(int x,int val)
{
    for(;x<=n;x+=lowbit(x))
        c[x]+=val;
}

int sum(int x)
{
    int s=0;
    while(x>0)
        s+=c[x],x-=lowbit(x);
    return s;
}

void dfs(int now)
{
    begin[now]=++index;
    value[index]=w[now];
    vis[now]=1;
    for(int i=head[now]; ~i; i=next[i])
        if(!vis[to[i]])
            dfs(to[i]);
    end[now]=index;
}

int main()
{
    int u,v,m,t,k;
    scanf("%d",&t);
    for(int ca=1; ca<=t; ca++)
    {
        scanf("%d%d",&n,&k);
        init();
        int cnt=0;
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&w[i]);
            if(wflag[w[i]]==0)
            {
                wflag[w[i]]=++cnt;
                w[i]=cnt;
            }
            else w[i]=wflag[w[i]];
        }
        for(int i=0; i<=cnt; i++)
        {
            pos[i].clear();
            pos[i].push_back(0);
        }
        for(int i=0; i<n-1; i++)
        {
            scanf("%d%d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
        dfs(1);
        scanf("%d",&m);
        for(int i=0; i<m; i++)
        {
            scanf("%d",&u);
            q[i].l=begin[u];
            q[i].r=end[u];
            q[i].id=i;
        }
        sort(q,q+m,cmp);
        cnt=0;
        for(int i=1; i<=n; i++)
        {
            u=value[i];
            pos[u].push_back(i);
            int tmp=pos[u].size()-1;
            if(tmp>=k)
            {
                if(tmp>k)//消除上一次含K个U值的区间更新
                {
                    add(pos[u][tmp-k-1]+1,-1);
                    add(pos[u][tmp-k]+1,1);
                }
                //更新这一次含有K个U值的区间
                add(pos[u][tmp-k]+1,1);
                add(pos[u][tmp-k+1]+1,-1);
            }
            while(q[cnt].r==i)
            {
                ans[q[cnt].id]=sum(q[cnt].l);
                cnt++;
            }
        }
        if(ca>1) puts("");
        printf("Case #%d:\n",ca);
        for(int i=0; i<m; i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值