HDU Boring counting 4358 莫队算法

本文介绍了一种利用树形结构DFS遍历和颜色离散化技术解决子树中特定颜色出现次数的问题,通过莫队算法优化查询效率。详细阐述了颜色离散化、时间戳记录、区间查询与排序等关键步骤。

题目大意:给一颗N个节点的树,每个节点上有一个颜色。给出Q组询问,每次询问一个子树上恰好出现K次的颜色的个数。

做法:
- 将树DFS一遍,记下时间戳,得到每个子树dfs序的对应区间,并将颜色离散化。
- 记下每个询问,将询问子树变成询问区间,将区间左端点除以n排序,在同一块的询问按右端点排序。
- 接着按莫队算法移动区间,得到对应答案。

143560672015-08-0514:44:27Accepted 43581248MS17660K2603 BG++Kurama
#pragma comment(linker, "/STACK:1024000000,1024000000") 
//C++拓栈外挂
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <map>
#define MAXN 100001
#define MEM 90
//拓栈内存 90MB
using namespace std;
int n,k,T,qn,SQRT,w[MAXN],ans[MAXN],Tq[MAXN],Tj[MAXN];
//Tj用于莫队算法统计颜色数,ans保存答案
int first[MAXN],ct;
map<int,int>m;
//离散化颜色用
struct tree{int iT,oT;}t[MAXN];
//对应子树的端点
struct query{int q,NO;}q[MAXN];
//保存询问
struct edge
{
    int v,next;
    void add(int u,int _v)
    {
        v=_v;
        next=first[u];
        first[u]=ct++;
    }
}e[MAXN*2];

bool cmp(const query &a,const query &b)
{
    if(t[a.q].iT/SQRT == t[b.q].iT/SQRT)return t[a.q].oT<t[b.q].oT;
    return t[a.q].iT/SQRT < t[b.q].iT/SQRT;
}
//排序比较函数
void dfs(int p)
{
    t[p].iT=++T;
    Tq[T]=m[w[p]]; //保存节点离散化后颜色对应的序号
    for(int i=first[p];i;i=e[i].next)
        if(!t[e[i].v].iT) 
            dfs(e[i].v);
    t[p].oT=T;
}
//DFS记下时间戳
void init()
{
    m.clear();
    ct=1;
    scanf("%d%d",&n,&k);
    for(int i=1,cnt=0;i<=n;i++)
    {
        scanf("%d",&w[i]);
        if(!m[w[i] ])m[w[i] ]=++cnt;//离散化颜色
        Tj[i]=first[i]=t[i].iT=0;
    }
    for(int i=1,u,v;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        e[ct].add(u,v);
        e[ct].add(v,u);
    }
    T=0;
    dfs(1);
    scanf("%d",&qn);
    for(int i=0;i<qn;i++)
    {
        scanf("%d",&q[i].q);
        q[i].NO=i;
    }
    //记下询问顺序
    SQRT=sqrt(n+1);
    sort(q,q+qn,cmp);
}
void solve()
{
    int l=1,r=0,ANS=0,num;
    for(int i=0;i<qn;i++)
    {
        if(r<t[q[i].q].oT)
        {
            ++r;
            for(;r<=t[q[i].q].oT;++r)
            {
                num=Tj[Tq[r] ];
                if(num==k)ANS--;
                if(num+1==k)ANS++;
                Tj[Tq[r] ]++;
            }
            r=t[q[i].q].oT;
        }
        if(l>t[q[i].q].iT){
            --l;
            for(;l>=t[q[i].q].iT;--l)
            {
                num=Tj[Tq[l] ];
                if(num==k)ANS--;else
                if(num+1==k)ANS++;
                Tj[Tq[l] ]++;
            }
            l=t[q[i].q].iT;
        }
        if(r>t[q[i].q].oT)
        {
            for(;r>t[q[i].q].oT;--r)
            {
                num=Tj[Tq[r] ];
                if(num==k)ANS--;else
                if(num-1==k)ANS++;
                Tj[Tq[r] ]--;
            }
        }
        if(l<t[q[i].q].iT)
        {
            for(;l<t[q[i].q].iT;++l)
            {
                num=Tj[Tq[l] ];
                if(num==k)ANS--;else
                if(num-1==k)ANS++;
                Tj[Tq[l] ]--;
            }
        }
        ans[q[i].NO]=ANS;
    }
}
//莫队算法
extern int main2(void) __asm__ ("main2");
int main2()
{
    int t;
    scanf("%d",&t);
    for(int i=1;i<=t;i++)
    {
        init();
        solve();
        printf("%sCase #%d:\n",(i==1)?(""):("\n"),i);
        for(int j=0;j<qn;j++)printf("%d\n",ans[j]);
    }
    exit(0);
}
//g++拓栈外挂
int main()
{
    int size = MEM << 20;  
    char *p = (char *)malloc(size) + size;
    __asm__ __volatile__(
        "movq  %0, %%rsp\n"
        "pushq $exit\n" 
        "jmp main2\n"
        :: "r"(p));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值