hdu4358 线段树之离线累加处理

思路:

      1.用前向星建图,然后先把树处理成区间;

      2.对询问按右边界排序,将权重离散化;

      3.离线处理,sum[k]记录k~i之间的k次权值数;

      4.每次将k次的段+1,k+1次的段-1.

 

ACcode:

#include<cstdio>
#include<vector>
#include<iostream>
#include<algorithm>
using std::vector;
using std::sort;
const int nsize=111111;

int n,q,pos;
int *p[nsize];
int a[nsize],num[nsize];
int sum[nsize<<2],cov[nsize<<2];
int e[nsize<<1],next[nsize<<1],head[nsize];
vector<int> lst[nsize];

struct Line
{
    int l,r;
} line[nsize];

struct qry
{
    int id,l,r;
    bool operator <(const qry &cmp) const{
        return r<cmp.r;
    }
} qry[nsize];

void dfs(int rt)
{
    num[rt]=1;
    line[rt].l=pos;
    for (int i=head[rt]; i>0; i=next[i])
        if (!num[e[i]]) pos++,dfs(e[i]);
    line[rt].r=pos;
}

void build(int rt,int l,int r)
{
    sum[rt]=cov[rt]=0;
    if (l==r) return ;
    int m=(l+r)>>1;
    build(rt<<1,l,m);
    build(rt<<1|1,m+1,r);
}

bool cmp(int *a1,int *a2)
{
    return *a1<*a2;
}

void PushDown(int rt)
{
    sum[rt<<1]+=cov[rt];
    cov[rt<<1]+=cov[rt];
    sum[rt<<1|1]+=cov[rt];
    cov[rt<<1|1]+=cov[rt];
    cov[rt]=0;
}

void update(int rt,int l,int r,int L,int R,int v)
{
    if (L<=l&&r<=R)
    {
        sum[rt]+=v;
        cov[rt]+=v;
        return ;
    }
    if (cov[rt]) PushDown(rt);
    int m=(l+r)>>1;
    if (L<=m) update(rt<<1,l,m,L,R,v);
    if (R>m)  update(rt<<1|1,m+1,r,L,R,v);
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

int query(int rt,int l,int r,int ps)
{
    if (l==r) return sum[rt];
    if (cov[rt]) PushDown(rt);
    int m=(l+r)>>1;
    if (ps<=m) return query(rt<<1,l,m,ps);
    else return query(rt<<1|1,m+1,r,ps);
}

void add(int u,int v,int i)
{
    e[i]=v;
    next[i]=head[u];
    head[u]=i;
}

int main()
{
    int T,cas=0;
    int k,i,j,u,v,rt,top;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d %d",&n,&k);
        for (top=i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            p[i]=&a[i];
            lst[i].clear();
            head[i]=num[i]=0;
        }
        for (i=1; i<n; i++)
        {
            scanf("%d %d",&u,&v);
            add(u,v,i+i-1);
            add(v,u,i+i);
        }
        pos=1,dfs(1);
        scanf("%d",&q);
        for (i=1; i<=q; i++)
        {
            qry[i].id=i;
            scanf("%d",&u);
            qry[i].l=line[u].l;
            qry[i].r=line[u].r;
        }
        sort(p+1,p+n+1,cmp);
        sort(qry+1,qry+q+1);
        v=*p[1],*p[1]=1,u=1;
        for (i=2; i<=n; i++)
        {
            if (*p[i]!=v) v=*p[i],u++;
            *p[i]=u;
        }
        build(1,1,n);
        for (j=i=1; i<=q; i++)
        {
            while (j<=qry[i].r&&j<=n)
            {
                v=a[j];
                lst[v].push_back(j);
                u=lst[v].size();
                if (u==k) update(1,1,n,1,lst[v][u-k],1);
                else if (u>k)
                {
                    update(1,1,n,lst[v][u-k-1]+1,lst[v][u-k],1);
                    update(1,1,n,(u>k+1?lst[v][u-k-2]:0)+1,lst[v][u-k-1],-1);
                }
                j++;
            }
            num[qry[i].id]=query(1,1,n,qry[i].l);
        }
        if (cas) printf("\n");
        printf("Case #%d:\n",++cas);
        for (i=1; i<=q; i++) printf("%d\n",num[i]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值