Hdu-5438 Boring counting(可持久化线段树)

185 篇文章 0 订阅
18 篇文章 0 订阅

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

题意:静态问区间中出现次数为k的数字的个数。

分析:通过dfs序可以将树上的问题转化为区间问题,然后可以通过将操作离线按右端点排序用每次用树状数组更新答案,同样可以用可持久化线段树转换成在线问题。


#include<iostream>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<cstring>
#include<stack>
#include<queue>
#define INF 0x3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define MAXN 100005
using namespace std;
typedef long long ll;
int T,n,k,q,u,v,cnt,num,dfs_cnt,w[MAXN],fh[MAXN],rd[MAXN],cd[MAXN],rt[MAXN],b[MAXN],sta[MAXN],Next[MAXN],pre[MAXN],temp[MAXN],sum[MAXN];
vector <int> G[MAXN];
struct Tree
{
    int ls,rs,lazy;
}tr[MAXN*40];
void Build(int &node,int l,int r)
{
    node = ++cnt;
    tr[node].lazy = 0;
    if(l == r) return;
    int mid = (l+r) >> 1;
    Build(tr[node].ls,l,mid);
    Build(tr[node].rs,mid+1,r);
}
void deal(int &node,int x,int y,int d,int l,int r)
{
    tr[cnt+1] = tr[node];
    node = ++cnt;
    if(x == l && y == r)
    {
        tr[node].lazy += d;
        return;
    }
    int mid = (l+r) >> 1;
    if(y <= mid) deal(tr[node].ls,x,y,d,l,mid);
    else
     if(x <= mid)
     {
        deal(tr[node].ls,x,mid,d,l,mid);
        deal(tr[node].rs,mid+1,y,d,mid+1,r);
     }
     else deal(tr[node].rs,x,y,d,mid+1,r);
}
int Find(int node,int x,int l,int r)
{
    if(l == r) return tr[node].lazy;
    int mid = (l+r)>>1;
    if(x <= mid) return tr[node].lazy + Find(tr[node].ls,x,l,mid);
    else return tr[node].lazy + Find(tr[node].rs,x,mid+1,r);
}
void dfs(int u,int fa)
{
    rd[u] = ++dfs_cnt;
    fh[dfs_cnt] = u;
    for(int v : G[u])
     if(v != fa) dfs(v,u);
    cd[u] = dfs_cnt;
}
void Init()
{
    dfs_cnt = cnt = 0;
    memset(rt,0,sizeof(rt));
    memset(pre,0,sizeof(pre));
    memset(sum,0,sizeof(sum));
    memset(Next,0,sizeof(Next));
    memset(temp,0,sizeof(temp));
    for(int i = 1;i <= n;i++) G[i].clear();
}
int main()
{
    scanf("%d",&T);
    for(int t = 1;t <= T;t++)
    {
        Init();
        if(t > 1) printf("\n");
        printf("Case #%d:\n",t);
        scanf("%d%d",&n,&k);
        for(int i = 1;i <= n;i++)
        {
            scanf("%d",&w[i]);
            b[i] = w[i];
        }
        sort(b+1,b+1+n);
        for(int i = 1;i <= n;i++) w[i] = lower_bound(b+1,b+1+n,w[i]) - b;
        for(int i = 1;i < n;i++)
        {
            scanf("%d%d",&u,&v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs(1,-1);
        Build(rt[0],1,n);
        for(int i = 1;i <= n;i++)
        {
            int color = w[fh[i]];
            if(temp[color]) Next[temp[color]] = i;
            pre[i] = temp[color];
            temp[color] = i;
        }
        for(int i = 1;i <= n;i++)
        {
            int color = w[fh[i]];
            rt[i] = rt[i-1];
            if(!sum[color]) sta[color] = i;
            sum[color]++;
            if(sum[color] == k) deal(rt[i],1,sta[color],1,1,n);
            else
             if(sum[color] > k)
             {
                 deal(rt[i],pre[sta[color]]+1,sta[color],-1,1,n);
                 deal(rt[i],sta[color]+1,Next[sta[color]],1,1,n);
                 sta[color] = Next[sta[color]];
             }
        }
        scanf("%d",&q);
        for(int i = 1;i <= q;i++)
        {
            scanf("%d",&num);
            printf("%d\n",Find(rt[cd[num]],rd[num],1,n));
        }
    }
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值