HDU 4358-I - Boring counting-dfs序+离线+树状数组/线段树

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


题意:

给你一颗树,n个节点,每个有其权值。给一个k。

q次询问,每次询问 以x为根节点的子树里,有多少种权值恰好出现次数为k。


我们先求个dfs序,把树型结构转为线性数组。

那么题目变成q次查询,每次查询区间L【x】,R【x】之间有多少个权值,出现的次数恰好为k


而本题可以用离线的做法,先把所有查询的区间按右端点排序。

 

pos[x][j],代表值为x的数在J次出现的位置

接下来是用dfs序数组建树的过程,显然当我们用1-i 的节点建树之后,i+1之后的节点不会影响 所有【 以i为右端点的区间查询】

因此我们 边插入 边查询【j,i】(j<=i) 的答案

插入一个x,假设其出现次数为y,当y<k时,显然对答案无影响,当y>=k时

会影响的是(pos[x][y-k],pos[x][y-k+1]】这个区间所有的数,显然这个区间的任意点z,构成的区间【z,当前最右边】这部分的答案会+1,

同时,当y>k的情况下,还会导致【p[x][y-k-1],p[x][y-k]】这个区间的任意点z,构成的区间【z,到最右边】这部分答案-1 (因为y超过了k嘛) 


 因此我们从左到右 依次把dfs序数组的数插进树状数组,每次插入后,可以查询到【j,i】的正确答案,插入n次的过程可以查到所有区间的询问。


。。对区间+1 -1部分直接用线段树的区间更新比较好写咯


线段树写法:

 

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <vector>
#define  inf 0x7fffffff
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 50005;
using  namespace std;

int  n,m;
    const int N = 100005  ;
struct tree
{

    int  sum[4*N], add[4*N] ;
    void pushDown(int i, int l, int r)
    {
        if(add[i] != 0)
        {
            int mid = (l + r) >> 1;
            add[i << 1] += add[i];
            sum[i << 1] += (mid - l + 1) * add[i];  //[l, mid]代表左儿子区间
            add[i << 1 | 1] += add[i];
            sum[i << 1 | 1] += (r - mid) * add[i];  //[mid + 1, r]代表右儿子区间
            add[i] = 0;
        }
    }
    void update(int i, int l, int r, int ql, int qr, int val) //更新区间为qlqr,当前区间为l,r,代表当前区间和的节点为i,更新值为val,
    {
        if(l > qr || ql > r)
            return ;
        if(l >= ql && r <= qr)
        {
            sum[i] += (r - l + 1) * val;
            add[i] += val;
            return ;
        }
        pushDown(i, l, r);
        int mid = (l + r) >> 1;
        update(i << 1, l, mid, ql, qr, val);
        update(i << 1 | 1, mid + 1, r, ql, qr, val);
        sum[i] = sum[i << 1] + sum[i << 1 | 1];
    }

    int query(int i, int l, int r, int ql, int qr)	 //查询区间为qlqr,当前区间为l,r,代表当前区间和的节点为i
    {
        if(l > qr || ql > r)
            return 0;
        if(l >= ql && r <= qr)
            return sum[i];
        pushDown(i, l, r);
        int mid =( l + r) >> 1;
        return query(i << 1, l, mid, ql, qr)
               + query(i << 1 | 1, mid + 1, r, ql, qr);
    }
};
tree tp;
vector < vector<int> >  mp(100000+50);
int id;
int in[100000+50],out[100000+50];
int ren[100005];
int vis[100005];
void dfs1(int x)
{
    in[x]=++id;
    ren[id]=x;
    vis[x]=1;
    int i;
    for (i=0; i<mp[x].size(); i++)
    {
        int v=mp[x][i];
        if (vis[v])continue;
        dfs1(v);
    }
    out[x]=id;
}
int dis[100005];
int aa[100005];
vector<int>pos[100005];
struct node
{
    int x,y,id;
    node() {}
    node(int a,int b)
    {
        x=a,y=b;
    }
};
node atm[100005];
bool cmp(node a,node b)
{
    return a.y<b.y;
}
int ans[100005];
int main()
{
    int t;
    cin>>t;
    int cnt=1;
    while(t--)
    {
        int  k,i,j;
        int x,y;
        memset(vis,0,sizeof vis);
        memset(tp.sum,0,sizeof tp.sum);
        memset(tp.add,0,sizeof tp.add);
        id=0;
        for (i=1; i<=n; i++) mp[i].clear();
        cin>>n>>k;
        for (i=1; i<=n; i++)
            scanf("%d",&aa[i]),dis[i]=aa[i];
        sort(dis+1,dis+1+n);
        int sz=unique(dis+1,dis+1+n)-dis-1;
        for (i=1; i<=sz; i++)  pos[i].clear(),pos[i].push_back(0);      //初始化第0个位置
        for (i=1; i<=n-1; i++)
        {
            scanf("%d%d",&x,&y);
            mp[x].push_back(y);
            mp[y].push_back(x);
        }
        dfs1(1);        //求dfs序
        int q;
        cin>>q;
        for ( i=1; i<=q; i++)
        {
            scanf("%d",&x);
            atm[i].x=in[x];         //管辖区间范围
            atm[i].y=out[x];
            atm[i].id=i;
        }
        sort(atm+1,atm+1+n,cmp);

        j=1;
        for ( i=1; i<=n; i++)
        {
            x=ren[i ];
            x=lower_bound(dis+1,dis+1+sz,aa[x])-dis;        //离散化对应下标
            pos[x].push_back(i);
            y=pos[x].size()-1;                    //出现过y次
            if (y>=k)
            {
                tp.update(1,1,n,pos[x][y-k]+1,pos[x][y-k+1],1);
                if (y>k)                              //答案-1的区间
                {
                       tp.update(1,1,n,pos[x][y-k-1]+1,pos[x][y-k],-1);
                }
            }
            for (; j<=q&&atm[j].y==i; j++)          //处理i为右端点的询问
                ans[atm[j].id]=tp.query(1,1,n,atm[j].x,atm[j].x);//printf("%d",tp.get(atm[j].x));
        }
        printf("Case #%d:\n",cnt++);
        for (i=1; i<=q; i++)
            printf("%d\n",ans[i]);
        if (t)printf("\n");
    }

    return 0;
}


树状数组写法:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <vector>
#define  inf 0x7fffffff
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 50005;
using  namespace std;

int  n,m;
struct tree
{
    int tree[100000+5];
    int lowbit(int x)
    {
        return x&-x;
    }
    void add(int x,int value)
    {
        for (int i=x; i<=n; i=i+lowbit(i))
        {
            tree[i]+=value;
        }
    }
    int get(int x)
    {
        int sum=0;
        for (int i=x; i; i-=lowbit(i))
    {
        sum+=tree[i];
    }
    return sum;
}
};
tree tp;
vector < vector<int> >  mp(100000+50);
int id;
int in[100000+50],out[100000+50];
int ren[100005];
int vis[100005];
void dfs1(int x)
{
    in[x]=++id;
    ren[id]=x;
    vis[x]=1;
    int i;
    for (i=0; i<mp[x].size(); i++)
    {
        int v=mp[x][i];
        if (vis[v])continue;
        dfs1(v);
    }
    out[x]=id;
}
int dis[100005];
int aa[100005];
vector<int>pos[100005];
struct node
{
    int x,y,id;
    node(){}
    node(int a,int b)
    {
        x=a,y=b;
    }
};
node atm[100005];
bool cmp(node a,node b)
{
    return a.y<b.y;
}
int ans[100005];
int main()
{
    int t;
    cin>>t;
    int cnt=1;
    while(t--)
    {
        int  k,i,j;
        int x,y;
        memset(vis,0,sizeof vis);
        memset(tp.tree,0,sizeof tp.tree);
        id=0;
        for (i=1; i<=n; i++) mp[i].clear();
        cin>>n>>k;
        for (i=1; i<=n; i++)
            scanf("%d",&aa[i]),dis[i]=aa[i];
        sort(dis+1,dis+1+n);
        int sz=unique(dis+1,dis+1+n)-dis-1;
        for (i=1; i<=sz; i++)  pos[i].clear(),pos[i].push_back(0);      //初始化第0个位置
        for (i=1; i<=n-1; i++)
        {
            scanf("%d%d",&x,&y);
            mp[x].push_back(y);
            mp[y].push_back(x);
        }
        dfs1(1);        //求dfs序
        int q;
        cin>>q;
        for ( i=1; i<=q; i++)
        {
            scanf("%d",&x);
            atm[i].x=in[x];         //管辖区间范围
            atm[i].y=out[x];
            atm[i].id=i;
        }
        sort(atm+1,atm+1+n,cmp);

        j=1;
        for ( i=1; i<=n; i++)
        {
            x=ren[i ];
            x=lower_bound(dis+1,dis+1+sz,aa[x])-dis;        //离散化对应下标
            pos[x].push_back(i);
              y=pos[x].size()-1;                    //出现过y次
            if (y>=k)
            {
                tp.add(pos[x][y-k]+1,1);            //答案+1的区间
                tp.add(pos[x][y-k+1]+1,-1);
                if (y>k)                              //答案-1的区间
                {
                tp.add(pos[x][y-k-1]+1,-1);
                tp.add(pos[x][y-k]+1,1);
                }
            }
            for (;j<=q&&atm[j].y==i;j++)            //处理i为右端点的询问
                ans[atm[j].id]=tp.get(atm[j].x);//printf("%d",tp.get(atm[j].x));
        }
        printf("Case #%d:\n",cnt++);
        for (i=1;i<=q;i++)
        printf("%d\n",ans[i]);
        if (t)printf("\n");
    }

    return 0;
}


你可能感兴趣的:(HDU 4358-I - Boring counting-dfs序+离线+树状数组/线段树)