HDOJ 4358 Boring counting

先dfs一下把树转换成对应的数组,对每一个询问就相当于求每个区间内出现k次的不同数的个数。然后类似HDOJ3874就可以用树状数组来维护了,运用离线的思想,把询问的区间按右端点排序,再预处理出每个数出现的前一次的位置pre和前k-1次的位置pk。

pre[pre[pk[i]]]<------1---->pre[pk[i]]<----2------>pk[i]<----3------->i

对于i号位置上的数,如果pk[i]存在,则对于右端点是位置i的询问来说如果左端点在2段内,则i位置上的数至少出现了k次(对应树状数组更新(pre[pk[i]]+1,+1) , (pk[i]+1,-1)  )。如果在pk之前还有这个数还出现过,也就是说如果询问的左端点落在了1段内,那么这个数出现的次数就大于k了这个数就不能算了(对应树状数组更新(pre[pre[pk[i]]]+1,-1) , (pre[pk[i]+1,+1)),所以要把数组给更新回来。。。。这样就只要SUM(询问左端点)就是答案。

因为数的大小没有意义,为了方便可以先离散化一下。

『网上好多代码都是错的,可以去Discuss里看一下那组样例』


Boring counting

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


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
 


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

#pragma comment(linker, "/STACK:102400000,102400000")

using namespace std;

const int maxn=100100;

int n,Q,K,val[maxn];
/****************数状数组***********************/
int tree[maxn];
int lowbit(int x)
{
    return x&(-x);
}
void ADD(int p,int v)
{
    for(int i=p;i<=n;i+=lowbit(i))
        tree[i]+=v;
}
int SUM(int p)
{
    int sum=0;
    for(int i=p;i;i-=lowbit(i))
        sum+=tree[i];
    return sum;
}

/****************前向星*************************/
struct Edge
{
    int to,next;
}edge[maxn*2];
int Adj[maxn],Size;
void build_init()
{
    Size=0; memset(Adj,-1,sizeof(Adj));
}
void Add_Edge(int u,int v)
{
    edge[Size].to=v;
    edge[Size].next=Adj[u];
    Adj[u]=Size++;
}

/*****************dfs树转数组************************/
int time=1;
int ax[maxn];

struct Interval
{
    int l,r;
}I[maxn];
void dfs(int x,int fa)
{
    I[x].l=time;
    for(int i=Adj[x];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa) continue;
        dfs(v,x);
        time++;
    }
    ax[time]=val[x];
    I[x].r=time;
}

/*******************Lisan*****************************/
int r[maxn],md[maxn],m;
bool cmpR(int a,int b)
{
    return val[a]<val[b];
}
void Lisan()
{
    for(int i=0;i<=n;i++) r[i]=i;
    sort(r+1,r+1+n,cmpR);
    md[1]=val[r[1]];
    val[r[1]]=m=1;
    for(int i=2;i<=n;i++)
    {
        if(md[m]!=val[r[i]]) md[++m]=val[r[i]];
        val[r[i]]=m;
    }
}

/********************others**************************/
struct ASK
{
    int l,r,id;
}ask[maxn];
int ans[maxn];
bool cmp(ASK a,ASK b)
{
    if(a.r!=b.r) return a.r<b.r;
    return a.l<b.l;
}
int pre[maxn],pk[maxn],idx[maxn];

void getPRE()
{
    memset(idx,0,sizeof(idx));
    memset(pre,0,sizeof(pre));
    for(int i=1;i<=n;i++)
    {
        pre[i]=idx[val[i]];
        idx[val[i]]=i;
    }
}
void getPK()
{
    memset(idx,-1,sizeof(idx));
    memset(pk,0,sizeof(pk));
    for(int i=n;i>=1;i--)
    {
        if(idx[val[i]]==-1)
        {
            int pos=i,cur=K-1;
            while(pos&&cur)
            {
                cur--;
                pos=pre[pos];
            }
            pk[i]=pos;
            idx[val[i]]=pk[i];
        }
        else
        {
            pk[i]=pre[idx[val[i]]];
            idx[val[i]]=pk[i];
        }
    }
}

int main()
{
    int T,cas=1;
    scanf("%d",&T);
while(T--)
{
    scanf("%d%d",&n,&K);
    memset(val,0,sizeof(val));
    for(int i=1;i<=n;i++) scanf("%d",&val[i]);
    build_init();
    for(int i=0;i<n-1;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        Add_Edge(u,v);
        Add_Edge(v,u);
    }

    time=1;
    dfs(1,-1);

    memcpy(val,ax,sizeof(ax));
    Lisan();

    getPRE();
    getPK();
    scanf("%d",&Q);

    for(int i=0;i<Q;i++)
    {
        int xx;
        scanf("%d",&xx);
        ask[i].l=I[xx].l,ask[i].r=I[xx].r,ask[i].id=i;
    }
    sort(ask,ask+Q,cmp);

    memset(tree,0,sizeof(tree));
    memset(ans,0,sizeof(ans));

    int pnt=0;
    for(int i=1;i<=n;i++)
    {
        if(pk[i])
        {
            int x=pre[pk[i]];
            if(x)
            {
                ADD(pre[x]+1,-1);
                ADD(x+1,1);
            }
            ADD(x+1,1);
            ADD(pk[i]+1,-1);
        }
        while(pnt<Q&&ask[pnt].r==i)
        {
            ans[ask[pnt].id]=SUM(ask[pnt].l);
            pnt++;
        }
    }

    printf("Case #%d:\n",cas++);
    for(int i=0;i<Q;i++)
        printf("%d\n",ans[i]);
    if(T) putchar(10);
}
    return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值