HDU 4358 Boring Counting ★★(2012 MultiUniversity Training Contest 6)

问题抽象区间内恰好出现K次的数的个数。 ------------------------------------------------------------------ UESTC出的题就是神啊T_T。。。一开始想了个函数式线段树方法后来发现错了=。=,然后也没什么思路,就是找着官方题解的方法做的。 思路: 题解说的用树状数组,这里当然也可以用线段树维护,和上面一样,线段树第j个数表示区间[j, i]内出现k次的数有多少个,然后像题解一样维护即可。(这种维护方法值得好好研究&&学习呀~) 代码中也有比较详细的注释:
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define MID(x,y) ((x+y)>>1)

using namespace std;
const int maxn = 100100;

int id,n,K;
int ans[maxn];
int w[maxn],wb[maxn];
int vis[maxn];
int a[maxn];            //线性权值
int l[maxn],r[maxn];    //线性区间
vector  v[maxn];   //边表
vector  pos[maxn]; //记录某数出现的位置
int num[maxn];          //记录某个数出现多少次了
map   M;      //离散化
int mtot;

struct ANS
{
    int l,r;
    int id;
}Q[maxn];

bool cmp(ANS a1, ANS a2)
{
    return a1.r ::iterator vp;
        if (v[x].size())
            for (vp = v[x].begin(); vp != v[x].end(); vp ++)
                dfs(*vp);
        r[x] = id;
    }
}

int sum[maxn<<2],add[maxn<<2];
void build(int l,int r,int rt)
{
    sum[rt] = 0;
    add[rt] = 0;
    if (l == r)  return ;
    int mid = MID(l,r);
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
}
void pushdown(int rt,int w)
{
    if (add[rt])
    {
        add[rt<<1] += add[rt];
        add[rt<<1|1] += add[rt];
        sum[rt<<1] += add[rt] * (w - (w >> 1));
        sum[rt<<1|1] += add[rt] * (w >> 1);
        add[rt] = 0;
    }
}
void update(int s,int t,int v,int l,int r,int rt)
{
    if (s <= l && r <= t)
    {
        sum[rt] += v * (r - l + 1);
        add[rt] += v;
        return ;
    }
    pushdown(rt, r-l+1);
    int mid = MID(l,r);
    if (s <= mid)   update(s,t,v,l,mid,rt<<1);
    if (mid < t)    update(s,t,v,mid+1,r,rt<<1|1);
}
int query(int p,int l,int r,int rt)
{
    if (l == p && r == p)
    {
        return sum[rt];
    }
    pushdown(rt,r-l+1);
    int mid = MID(l,r);
    if (p <= mid)   return query(p,l,mid,rt<<1);
    else return query(p,mid+1,r,rt<<1|1);
}
int main()
{
    //freopen("data.txt","r+",stdin);
    int tt,caseo = 1;
    scanf("%d",&tt);
    while(tt--)
    {
        //Initialize
        mtot = id = 0;
        memset(v,0,sizeof(v));
        memset(vis,0,sizeof(vis));
        memset(pos,0,sizeof(pos));
        memset(num,0,sizeof(num));
        //input
        printf("Case #%d:\n",caseo ++);
        scanf("%d%d",&n,&K);
        for (int i = 1; i <= n; i ++)
            scanf("%d",&w[i]);
        for (int i = 1; i < n; i ++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            v[a].push_back(b);  //边表
        }
        //树形结构转线性结构
        dfs(1);
        M.clear();

        //权值离散化
        for (int i = 1; i <= n; i ++)
            if (!M[a[i]])   M[a[i]] = ++ mtot;

        int q;
        scanf("%d",&q);
        for (int i = 0; i < q; i ++)
        {
            int p;
            scanf("%d",&p);
            Q[i].l = l[p];
            Q[i].r = r[p];
            Q[i].id = i;
        }
        sort(Q,Q+q,cmp);
        int pt = 0;
        build(1,n,1);
        for (int i = 1; i <= n; i ++)
        {
            pos[M[a[i]]].push_back(i);
            num[M[a[i]]] ++;
            if (num[M[a[i]]] >= K)
                if (num[M[a[i]]] == K)
                    update(1,pos[M[a[i]]][0],1,1,n,1);
                else
                {
                    int ss = (num[M[a[i]]] - K <= 2)?1:(num[M[a[i]]] - K - 2);
                    update(ss,pos[M[a[i]]][num[M[a[i]]]-K-1],-1,1,n,1);
                    update(pos[M[a[i]]][num[M[a[i]]]-K-1]+1,pos[M[a[i]]][num[M[a[i]]]-K],1,1,n,1);
                }
            else;
            while(pt < q && Q[pt].r == i)
            {
                ans[Q[pt].id] = query(Q[pt].l,1,n,1);
                pt ++;
            }
        }
        for (int i = 0; i < q; i ++)
            printf("%d\n",ans[i]);
        if (tt) printf("\n");
    }
    return 0;
}
举杯独醉,饮罢飞雪,茫然又一年岁。 ------AbandonZHANG
原文地址:https://www.cnblogs.com/AbandonZHANG/p/4113947.html