hdu4358Boring counting

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4358

题意:给定一棵n个节点的树和k,每个点有点权,再给q个询问,每次询问:在以v为根的子树中有多少个数恰好出现了k次。

分析:遇到子树问题,首先考虑到的就是dfs序,然后发现还有一个条件k。我的处理方式是:先离散化,然后构出dfs序,然后处理出in[],out[],lose[]数组,如果当前i中的数是某个出现次数大于等于k次的数,那么in[i]表示刚好构成k个数的初始位置,out[i]表示a[i]在下一次出现的位置的前一位(不出现了就记为n),lose[i]记录a[i]向前数第k+1个,如果i前面恰好k个a[i]那么lose[i]=0。这样我们按询问的右端点排序,每次加入i的3个值(in[i]插入1,out[i]和lose[i]插入-1),然后查询在[l,r]中有多少个1即可。我处理得有些麻烦。O(qlogn)

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=100100;
const int MAX=151;
const int mod=100000000;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=998244353;
const ll INF=10000000010;
typedef double db;
typedef unsigned long long ull;
int tot,u[N],v[2*N],pre[2*N];
void add(int x,int y) {
    v[tot]=y;pre[tot]=u[x];u[x]=tot++;
}
int k,a[N],w[N],in[N],out[N];
void dfs(int x,int y) {
    k++;in[x]=k;a[k]=w[x];
    for (int i=u[x];i!=-1;i=pre[i])
    if (v[i]!=y) dfs(v[i],x);
    out[x]=k;
}
struct node {
    int l,r,id;
}qu[N];
int cmd(node x,node y) { return x.r<y.r; }
int f[N],ans[N],sub[N],fir[N],lose[N];
void add_x(int x,int y,int n) {
    if (x<1) return ;
    for (;x<=n+1;x+=x&(-x)) f[x]+=y;
}
int getsum(int x) {
    int ret=0;
    for (;x;x-=x&(-x)) ret+=f[x];
    return ret;
}
int main()
{
    int i,n,m,q,t,x,y,ca,R;
    scanf("%d", &t);
    for (ca=1;ca<=t;ca++) {
        scanf("%d%d", &n, &m);
        for (i=1;i<=n;i++) {
            scanf("%d", &w[i]);a[i]=w[i];
        }
        tot=0;memset(u,-1,sizeof(u));
        for (i=1;i<n;i++) {
            scanf("%d%d", &x, &y);add(x,y);add(y,x);
        }
        sort(a+1,a+n+1);
        k=unique(a+1,a+n+1)-(a+1);
        for (i=1;i<=n;i++) w[i]=lower_bound(a+1,a+k+1,w[i])-a;
        k=0;dfs(1,1);
        scanf("%d", &q);
        for (i=1;i<=q;i++) {
            scanf("%d", &x);qu[i].id=i;
            qu[i].l=in[x];qu[i].r=out[x];
        }
        sort(qu+1,qu+q+1,cmd);
        memset(u,0,sizeof(u));
        memset(v,0,sizeof(v));
        memset(w,0,sizeof(w));
        memset(sub,0,sizeof(sub));
        memset(pre,0,sizeof(pre));
        memset(fir,0,sizeof(fir));
        memset(lose,0,sizeof(lose));
        for (i=1;i<=n;i++)
        if (!fir[a[i]]) fir[a[i]]=i;
        for (i=1;i<=n;i++) {
            pre[i]=w[a[i]];sub[w[a[i]]]=i;
            w[a[i]]=i;u[a[i]]++;
            if (u[a[i]]>=m) {
                v[i]=1;
                if (u[a[i]]==m) { in[i]=fir[a[i]];out[i]=n; }
                else { out[pre[i]]=i-1;in[i]=sub[in[pre[i]]];out[i]=n;lose[i]=in[pre[i]]; }
            }
        }
        R=0;memset(f,0,sizeof(f));
        for (i=1;i<=q;i++) {
            while (R<qu[i].r) {
                R++;
                if (v[R]) {
                    if (v[pre[R]]&&pre[R]) { add_x(lose[pre[R]],1,n);add_x(in[pre[R]],-1,n);add_x(out[pre[R]]+1,1,n); }
                    add_x(lose[R],-1,n);add_x(in[R],1,n);add_x(out[R]+1,-1,n);
                }
            }
            ans[qu[i].id]=getsum(qu[i].r)-getsum(qu[i].l-1);
        }
        printf("Case #%d:\n", ca);
        for (i=1;i<=q;i++) printf("%d\n", ans[i]);
        if (ca!=t) printf("\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值