hdu 4358 Boring counting(dfs序+莫队算法)

题意:给出一棵树,每个结点有一个权值,有q个查询,查询以u的子树中出现的相同权值的次数为k的数的个数。

思路:首先利用dfs可以把某个结点的子树转化成一个线性区间,然后就可以开心的用莫队搞啦~写完看了看网上的做法,发现大部分都是用线段树或树状数组过的,方法也比较巧妙……不过还是莫队比较好写啊,某些情况下莫队真是神器,时间也不太糟糕800ms+

代码:

#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
const int size=255;
struct Edge
{
    int v,next;
    Edge(){}
    Edge(int v,int next):v(v),next(next){}
}edges[maxn];
struct Query
{
    int L,R,id;
    Query(){}
    Query(int L,int R,int id):L(L),R(R),id(id){}
    bool operator <(const Query & a) const
    {
        if(L/size!=a.L/size) return L<a.L;
        return R<a.R;
    }
}querys[maxn];
int val[maxn],num[maxn],cnt[maxn],Lv[maxn],Rv[maxn];
int head[maxn],ans[maxn],nEdge,dfs_clock,n,m,k,tot;
map<int,int>mp;
void Init()
{
    mp.clear();
    memset(head,0xff,sizeof(head));
    nEdge=-1;dfs_clock=0;
    memset(cnt,0,sizeof(cnt));
}
void AddEdges(int u,int v)
{
    edges[++nEdge]=Edge(v,head[u]);
    head[u]=nEdge;
    edges[++nEdge]=Edge(u,head[v]);
    head[v]=nEdge;
}
void dfs(int u,int fa)
{
    Lv[u]=++dfs_clock;
    val[dfs_clock]=num[u];
    for(int k=head[u];k!=-1;k=edges[k].next)
    {
        int v=edges[k].v;
        if(v==fa) continue;
        dfs(v,u);
    }
    Rv[u]=dfs_clock;
}
inline void Add(int x)
{
    if(cnt[val[x]]==k) tot--;
    if(cnt[val[x]]+1==k) tot++;
    cnt[val[x]]++;
}
inline void Dec(int x)
{
    if(cnt[val[x]]==k) tot--;
    if(cnt[val[x]]-1==k) tot++;
    cnt[val[x]]--;
}
void solve()
{
    tot=0;
    int l=1,r=0;
    for(int i=0;i<m;++i)
    {
        while(querys[i].L<l) Add(--l);
        while(querys[i].R<r) Dec(r--);
        while(querys[i].L>l) Dec(l++);
        while(querys[i].R>r) Add(++r);
        ans[querys[i].id]=tot;
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t,tcase=0;
    scanf("%d",&t);
    while(t--)
    {
        if(tcase) printf("\n");
        tcase++;
        Init();
        scanf("%d%d",&n,&k);
        int u=0,v;
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&v);
            if(mp[v]) num[i]=mp[v];
            else num[i]=mp[v]=++u;
        }
        for(int i=1;i<n;++i)
        {
            scanf("%d%d",&u,&v);
            AddEdges(u,v);
        }
        dfs(1,-1);
        scanf("%d",&m);
        for(int i=0;i<m;++i)
        {
            scanf("%d",&u);
            querys[i]=Query(Lv[u],Rv[u],i);
        }
        sort(querys,querys+m);
        solve();
        printf("Case #%d:\n",tcase);
        for(int i=0;i<m;++i)
            printf("%d\n",ans[i]);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值