HDU 4358 Boring counting(树的遍历+树状数组+离散化+离线处理)

来源:互联网 发布:java导出excel到前端 编辑:程序博客网 时间:2024/05/21 22:48

HDU 4358 Boring counting(树的遍历+树状数组+离散化+离线处理)

分析:

       首先读入所有的节点权重存在nodes[n]中,然后读入n-1条边,1为根节点.把整个图做成一个邻接表的形式.用vector<int> g[n],其中g[i]表示与i邻接的点都有哪些.然后对1号节点执行中序遍历.

首先本题是对整棵子树的查询,其实我们可以对该树执行一次后序遍历,把我们访问到的节点编号一一保存到nodes[i].index=j中 (i是这个节点原来的编号,j是表示我们中序遍历这棵树时,访问该点时是第j个访问的).

现在每个根节点的查询其实就正好对应了一个连续的区间查询.(想想是不是)并把每个根节点所对应的区间L[i]=j1和R[i]=j2记下来. (j1和j2是节点新的编号,是在执行初始i节点的后序遍历后,所有节点重新编号后,查询的区间是[j1,j2])

       后序遍历后,给所有节点的权重重新编号后存在了a[i]中,我们如果要查询初始x节点的子树,只需要查询a[i]数组内在区间[L[x],R[x]]中符合要求的值有多少个.

       接下来我们首先把a[i]的值重新映射集中(因为a[i]最大可能10亿,但却只有10W个a[i]值)到数组b[i]中.

       离线处理每个查询,把每个查询按L从小到大排序.

       预处理:接下来我们建一棵树状数组A[n],然后从1到n扫描b[i]数组,如果对于b[i]=x的值是正好第k次出现了,我们就add(i,1),且我们需要找到b[i]值正好出现了第k+1次的位置y,执行add(y,-1).扫描完b[n]之后,我们保证sum(R)的值就是对所有区间[1,R]的查询结果.

       接下来我们需要查询所有[2,R]区间的结果,我们首先要消除b[1]对后续数列产生的影响,我们找到b[1]值第k次出现的位置y1和第k+1次出现的位置y2和第k+2次出现的位置y3(其实就是上一次针对b[1]的值执行add(,1)和add(,-1)的位置),所以我们执行add(y1,-1)和add(y2,2)和add(y3,-1). 消除影响后,b[1]就好像从来没有出现过一样.

       完成上面那步,我们保证sum(R)的值就是对所有区间[2,R]的查询结果.

当我们查询完sum(R)表示区间[i,R]的查询结果后,我们需要找到当前d[i]值从i位置开始第k次出现的位置y1(包括i,且d[i]就算是第一次出现的位置),和d[i]值第k+1次出现的位置y2,和d[i]值第k+2次出现的位置y3,执行add(y1,-1) 和 add(y2,2)  和add(y3,-1)

原始代码:超时

 

#include<cstdio>#include<cstring>#include<algorithm>#include<vector>using namespace std;const int MAXN =100000+100;const int MAXM =100000+100;struct node{    int v;    int index;    bool operator <(const node&b)const    {        return v<b.v;    }}nodes[MAXN],a[MAXN];//初始树的权值struct command{    int l,r;    int index;    bool operator <(const command&b)const    {        return l<b.l;    }}coms[MAXM];//查询int L[MAXN],R[MAXN],cnt;int b[MAXN];int ans[MAXM];int cur[MAXN];//cur[i]表示b[j]=i值当前处理阶段第k次出现的位置int first[2*MAXN],next[2*MAXN],u[2*MAXN],v[2*MAXN];void add(int i,int j,int num){    u[num]=i;    v[num]=j;    next[num]=first[u[num]];    first[i]=num;}int dfs(int fa,int a)//后序访问以u为根节点(fa是u的父节点)的树,并返回该树的最最左下角的点的新编号{    int min_num=cnt;    for(int e=first[a];e!=-1;e=next[e])if(v[e]!=fa)    {        dfs(a,v[e]);    }    L[a]=min_num;    R[a]=cnt;    nodes[a].index=cnt++;    return L[a];}struct HASHMAP{    int head[MAXN];    int next[MAXN];//把所有相同的b[i]值从前到后串联起来    void init()    {        memset(head,-1,sizeof(head));    }    int insert(int i,int v)    {        int num=1;//表示插入的这个b[i]是值为v的第num次出现了        next[i]=-1;        if(head[v]==-1)        {            head[v]=i;        }        else        {            int j=head[v];            num++;            while(next[j]!=-1)            {                j=next[j];                num++;            }            next[j]=i;        }        return num;    }    int find(int i)//找到b[i]值相同的后继元素的下标    {        if(i==-1)            return -1;        return next[i];    }}hm;int c[MAXN];int lowbit(int x){    return x&(-x);}int sum(int x){    int res=0;    while(x)    {        res +=c[x];        x-=lowbit(x);    }    return res;}void add(int x,int v){    while(x<MAXN)    {        c[x]+=v;        x+=lowbit(x);    }}int main(){    int T,kase=1;    scanf("%d",&T);    while(T--)    {        printf("Case #%d:\n",kase++);        int n,K;        scanf("%d%d",&n,&K);        for(int i=1;i<=n;i++)//读权重        {            scanf("%d",&nodes[i].v);        }        memset(first,-1,sizeof(first));        for(int i=1;i<n;i++)//读边        {            int u,v;            scanf("%d%d",&u,&v);            add(u,v,i*2-1);            add(v,u,i*2);        }        cnt=1;//记录当前递归到了第几个点        dfs(-1,1);        for(int i=1;i<=n;i++)        {            a[nodes[i].index].v = nodes[i].v;            a[nodes[i].index].index = nodes[i].index;        }/*        for(int i=1;i<=n;i++)        {            printf("%d %d %d\n",a[nodes[i].index],L[i],R[i]);        }*/        sort(a+1,a+n+1);        int max_num=1;        b[a[1].index]=1;        for(int i=2;i<=n;i++)        {            if(a[i].v==a[i-1].v)                b[a[i].index]=b[a[i-1].index];            else                b[a[i].index]=++max_num;        }        hm.init();        memset(cur,-1,sizeof(cur));        for(int i=1;i<=n;i++)        {            int num = hm.insert(i,b[i]);            if(num==K)                cur[b[i]]=i;//b[i]值第K次出现的位置是i        }/*        for(int i=1;i<=n;i++)            printf("%d ",b[i]);        printf("\n");        for(int i=1;i<=n;i++)        {            printf("%d %d\n",L[i],R[i]);        }        printf("***************\n");*/        int Q;        scanf("%d",&Q);        for(int i=1;i<=Q;i++)        {            int root;            scanf("%d",&root);            coms[i].l=L[root];            coms[i].r=R[root];            coms[i].index=i;        }        sort(coms+1,coms+Q+1);        for(int i=1;i<=n;i++)//预处理        {            int y1=cur[i];            int y2=hm.find(y1);            if(y1>-1)            {                add(y1,1);                if(y2>-1)                    add(y2,-1);            }        }        int j=1;//表示当前处理排序后的第j条命令        for(int i=1;i<=n;i++)        {            while(coms[j].l==i)            {                ans[coms[j].index] = sum(coms[j].r);                j++;//别忘了            }            if(j>Q)                break;            int y1=cur[b[i]];            int y2=hm.find(y1);            int y3=hm.find(y2);            if(y1>0)            {                add(y1,-1);                if(y2>0)                {                    add(y2,2);                    if(y3>0)                    {                        add(y3,-1);                    }                }            }            cur[b[i]]=y2;//这一句别忘了,更新d[i]下一个第k次出现的位置        }        for(int i=1;i<=Q;i++)            printf("%d\n",ans[i]);    }    return 0;}

现在换另一种做法,前面几步都一样,知道预处理那里,现在不预处理了.而是将所有询问按照R从小到大排序(如果R相同,则按L从小到大排序).

我们从1到n读入每一个b[i]的值,并且用一个vector<int> vec[MAXN]记录vec[b[i]][k]=j表示b[i]值已经出现了k次且第k次出现在j位置.

如果当前size = vec[b[i]].size()-1正好等于k(因为vec[x][0]=0,顶掉了这个0位,所以多存了一个0),那么执行add( vec[b[i]][size-k]+1,1 ); 和add( vec[b[i]][size-k+1]+1,-1 );

此时我们保证sum(L)就是所有区间[L,i]的查询值(其中L是可变的,但是i是固定的也就是说只有当前查询存在R==i才查询,如果没有R==i,就继续读入下一个。)

如果当前size = vec[b[i]].size()-1正好大于k(因为vec[x][0]=0,顶掉了这个0位,所以多存了一个0),那么执行add( vec[b[i]][size-k-1]+1,-1 ); 和add( vec[b[i]][size-k]+1,2 ); 和 add( vec[b[i]][size-k+1]+1,-1 );

(自己在数轴上画图验证一下看看是不是)

1.下次如果需要离散化的数据,尽量先进行离散化在做其他操作。

2.由于没有初始化c数组,导致WA1小时,真是郁闷。以后全局变量和数组都在最前面统一初始化。

3.如果栈可能溢出,记得栈中的变量尽量少就不会溢出,否则就用自己手写的栈。

AC代码:765ms。

 

#include<cstdio>#include<cstring>#include<algorithm>#include<vector>using namespace std;//HDU开栈外挂,本代码不用也可以AC#pragma comment(linker, "/STACK:102400000,102400000")const int MAXN =100000+5000;const int MAXM =100000+5000;vector<int> vec[MAXN];struct node{    int v;    int index;    bool operator <(const node&b)const    {        return v<b.v;    }} nodes[MAXN],a[MAXN]; //初始树的权值struct command{    int l,r;    int index;    bool operator <(const command&b)const    {        return r<b.r ;    }} coms[MAXM]; //查询int L[MAXN],R[MAXN],cnt;int b[MAXN];int ans[MAXM];int cur[MAXN];//cur[i]表示b[j]=i值当前处理阶段第k次出现的位置int first[2*MAXN],next[2*MAXN],u[2*MAXN],v[2*MAXN];void add(int i,int j,int num){    u[num]=i;    v[num]=j;    next[num]=first[u[num]];    first[i]=num;}void dfs(int fa,int a)//后序访问以u为根节点(fa是u的父节点)的树,并返回该树的最最左下角的点的新编号{    L[a]=cnt;    for(int e=first[a]; e!=-1; e=next[e])if(v[e]!=fa)        {            dfs(a,v[e]);        }    R[a]=cnt;    nodes[a].index=cnt++;}int c[MAXN];int lowbit(int x){    return x&(-x);}int sum(int x){    int res=0;    while(x)    {        res +=c[x];        x-=lowbit(x);    }    return res;}void add(int x,int v){    while(x<MAXN)    {        c[x]+=v;        x+=lowbit(x);    }}int main(){    int T;    scanf("%d",&T);    for(int kase=1; kase<=T; kase++)    {        memset(first,-1,sizeof(first));        memset(c,0,sizeof(c));        cnt=1;//记录当前递归到了第几个点        int n,K;        scanf("%d%d",&n,&K);        for(int i=1; i<=n; i++) //读权重        {            scanf("%d",&nodes[i].v);        }        for(int i=1; i<n; i++) //读边        {            int u,v;            scanf("%d%d",&u,&v);            add(u,v,i*2-1);            add(v,u,i*2);        }        dfs(-1,1);        for(int i=1; i<=n; i++)        {            a[nodes[i].index].v = nodes[i].v;            a[nodes[i].index].index = nodes[i].index;        }        sort(a+1,a+n+1);        int max_num=1;        b[a[1].index]=1;        for(int i=2; i<=n; i++)        {            if(a[i].v==a[i-1].v)                b[a[i].index]=b[a[i-1].index];            else                b[a[i].index]=++max_num;        }        int Q;        scanf("%d",&Q);        for(int i=1; i<=Q; i++)        {            int root;            scanf("%d",&root);            coms[i].l=L[root];            coms[i].r=R[root];            coms[i].index=i;        }        sort(coms+1,coms+Q+1);        for(int i=1; i<=n; i++)        {            vec[i].clear();            vec[i].push_back(0);        }        int j=1;//表示当前处理排序后的第j条命令        for(int i=1; i<=n; i++)        {            vec[b[i]].push_back(i);            int size = vec[b[i]].size()-1;            if(size>=K)            {                if(size==K)                {                    add( vec[b[i]][size-K]+1,1 );                    add( vec[b[i]][size-K+1]+1,-1 );                }                else if(size>K)                {                    add( vec[b[i]][size-K-1]+1,-1 );                    add( vec[b[i]][size-K]+1,2 );                    add( vec[b[i]][size-K+1]+1,-1 );                }            }            while(coms[j].r==i && j<=Q)            {                ans[coms[j].index] = sum( coms[j].l );                j++;            }            if(j>Q)                break;        }        printf("Case #%d:\n",kase);        for(int i=1; i<=Q; i++)            printf("%d\n",ans[i]);        if(kase<T)            printf("\n");    }    return 0;}

0 0