zoukankan      html  css  js  c++  java
  • hdu 4358 Boring counting 树状数组

    题意:有N个节点,组成一棵树,1号节点是根节点。每个节点都有一个权值。现在有q个查询,每次查询根节点是U的子树中有多少个不同的权值恰好出现K次。

    思路:第一眼让人感觉是图论,但仔细分析发现单纯的搜索肯定会超时。

    这题分为另个部分:

    1.以u为根节点的子树有多少种不同的权值。

    2.权值为k的有多少。

    对于线性列表,查询一个区间有多少个不同的子节点,我们用一个pre[v]记录权值v上次出现的位置。那么当第i个数的权值为v时,区间pre[v]+1-->i这个区间就会多一个不同的数,那么可以用update(pre[v]+1,1)来更新。但是i之后的状态不能改变,故再用一个update(i+1,-1)。这样就保证了只有pre[v]+1-->i的区间改变了。

    所以,我们要对所有查询区间按右边界升序排序。当插入的值i为第j个查询的右边界时,其不同元素的个数就是对该查询的左节点求和,相当于该查询区间内有多少个不同的数v和pre[v]的连线与qt[i].r和qt[i].l的连线相交,若相交,必包含该查询的左节点qt[i].l,故只需对qt[i].l做求和操作。

    完成上面的问题,那么我们就来解决现在问题。首先要将所有的权值离散化,这样才能使用pre[v]。离散化过后我们从1号节点开始对树遍历一遍:

    void dfs(int u)
    {
        L[u]=++id;list[id]=art[u];//每个节点的左边界就是本身
        int i;
        for(i=index[u];i!=-1;i=edge[i].next)
        {
            if(!L[edge[i].to]) dfs(edge[i].to);
        }
        R[u]=id;//右边界就是子树中id编号最大的
    }

    这样我们就将查找以u为根节点转化为第u个区间的问题。
    然后然后,就是查找了。

    这里的pre[v][k]表示权值为v的第k次出现的位置。

    对于第i个元素来说,如果该节点的权值v出现的次数超过K,那么pre[v][i-k-1]+1,到pre[v-k]的区间的k出现次数就会超过k,那么用update(pre[v][i-k-1]+1,-1);

    并且要用update(pre[v][i-k]+1,1);以确保之后的区间不受影响。

    具体见代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<map>
    #include<vector>
    #include<cstring>
    using namespace std;
    struct QT{
        int l,r,id;
        bool operator <(const QT &temp) const
        {
            return r<temp.r;
        }
    }qt[100010];
    vector<int> pre[100010];
    struct Edge{
        int to,next;
    }edge[300020];
    int n,c[100010],e,index[100010],list[100010],art[100010],id=0;
    int L[100010],R[100010];
    int ans[100010];
    void addedge(int from,int to)
    {
        edge[e].to=to;
        edge[e].next=index[from];
        index[from]=e++;
        edge[e].to=from;
        edge[e].next=index[to];
        index[to]=e++;
    }
    int cmp(int x,int y)
    {
        return art[x]<art[y];
    }
    void update(int pos,int val)
    {
        while(pos<=n)
        {
            c[pos]+=val;
            pos+=pos&(-pos);
            //cout<<"ok"<<endl;
        }
    }
    int Sum(int i)
    {
        int s=0;
        while(i)
        {
            s+=c[i];
            i-=i&(-i);
        }
        return s;
    }
    void dis()
    {
        int r[100010],i;
        int temp=-1;
        for(i=1;i<=n;i++)
        r[i]=i;
        sort(r+1,r+1+n,cmp);
        int prev=art[r[1]]-1;
        for(i=1;i<=n;i++)
        {
            if(art[r[i]]!=prev) prev=art[r[i]],art[r[i]]=++temp;
            else
                art[r[i]]=temp;
        }
        for(i=0;i<=temp;i++)
        {
            pre[i].clear();
            pre[i].push_back(0);
        }
    }
    void dfs(int u)
    {
        L[u]=++id;list[id]=art[u];
        int i;
        for(i=index[u];i!=-1;i=edge[i].next)
        {
            if(!L[edge[i].to]) dfs(edge[i].to);
        }
        R[u]=id;
    }
    void init()
    {
        memset(c,0,sizeof(c));
        memset(L,0,sizeof(L));
        memset(R,0,sizeof(R));
        memset(index,-1,sizeof(index));
        id=0;
        e=0;
    }
    int main()
    {
        int i,j,q,cas,k,t,a,b;
        scanf("%d",&t);
        cas=0;
        while(t--)
        {
            init();
            scanf("%d%d",&n,&k);
            for(i=1;i<=n;i++)
                scanf("%d",&art[i]);
            dis();
            for(i=1;i<n;i++)
            {
                scanf("%d%d",&a,&b);
                addedge(a,b);
            }
            dfs(1);
            scanf("%d",&q);
            int x;
            for(i=1;i<=q;i++)
            {
                scanf("%d",&x);
                qt[i].l=L[x];
                qt[i].r=R[x];
                qt[i].id=i;
            }
            sort(qt+1,qt+q+1);
            j=1;
            //
            for(i=1;i<=n;i++)
            {//cout<<"ok"<<endl;
                int v=list[i];pre[v].push_back(i);
                int g=pre[v].size()-1;
                if(g>=k)
                {
                    if(g>k)
                    {
                        update(pre[v][g-k-1]+1,-1);
                        update(pre[v][g-k]+1,1);
                    }
                    update(pre[v][g-k]+1,1);
                    update(pre[v][g-k+1]+1,-1);
                }
                while(qt[j].r==i)
                {
                    ans[qt[j].id]=Sum(qt[j].l);
                    j++;
                }
            }
            printf("Case #%d:\n",++cas);
            for(i=1;i<=q;i++)
            {
                printf("%d\n",ans[i]);
            }
            if(t)
                printf("\n");
        }
        return 0;
    }
  • 相关阅读:
    [CSP校内集训]2019.10.16数学专题
    Knights of the Round Table(缩点+判奇环) poj 2942 && 洛谷SP2878
    机房测试7:exam(二进制+模拟)
    机房测试9:gift(单调队列优化dp)
    机房测试9:hotel(神奇dp)
    机房测试8:question(求最大1矩阵:悬线法 or 二分)
    机房测试7:paint(分治+st表)
    机房测试6:矿石(优先队列)
    机房测试6:括号序列(hash+栈 )
    bzoj1123 && 洛谷 P3469 tarjan割点的运用
  • 原文地址:https://www.cnblogs.com/wangfang20/p/3096620.html
Copyright © 2011-2022 走看看