hdu 4358 Boring counting

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

map 版本  

比赛的时候也用map 写了 不过没有加优化 所以超时了

调试了一上午  下午自己出数据测了一下才知道那里出错了 汗

大体思路:

用map<int , int > 保存子树某个数出现的次数 然后从叶子节点向上更新合并 合并的时候需要 size小的向size大

的上面合并 这样省时 这是由map 的构造决定的

用c++ 提交要 手动开栈  否则会栈溢出  用G++ 提交可以避免但花费时间要长一些

自测数据 对我来说很重要的一组数据 就是这里错了一上午

1
6 1
1 2 3 4 5 6
1 2
1 3
3 4
3 5
1 6
6
1
2
3
4
5
6

详情及其细节见代码及其注释:

#include<iostream>

#include<cstdio>

#include<cstring>

#include<algorithm>

#include<map>

#include<queue>

#include<cmath>

#define LL long long

//#pragma comment(linker, "/STACK:102400000,102400000")//c++提交的话 需要手动开栈(看解析上的) g++不用 但时间花费长



using namespace std;



const int N=100005;

int head[N];//邻接表 表头

struct node

{

    int j;

    int next;

}side[N*2];

int son[N];//儿子节点个数

int f[N];//保存父节点

int id[N];//某个节点对应相关数据存在了哪个map里面 由于合并的关系 刚开始 i和id[i]对应相等 但一定的合并后就变了

int ans[N];//保存答案 离线保存要用

map<int ,int>str[N];

map<int ,int>:: iterator it,it1;

queue<int>qu;

int K;

void build(int x,int i)

{

    side[i].next=head[x];

    head[x]=i;

}

void dfs(int x,int pre)//用dfs求的本节点的父节点 和儿子节点数  和将叶子节点加入队列

{

    int t=head[x];

    f[x]=pre;

    while(t!=-1)

    {

        if(side[t].j!=pre)

        {

          dfs(side[t].j,x);

          ++son[x];

        }

        t=side[t].next;

    }

    if(son[x]==0)

    {

        qu.push(x);

    }

}

void Add(int I,int i,int j)//将 map j 合并到i当中 将答案ans[I]进行更新

{

    for(it=str[j].begin();it!=str[j].end();++it)//it 遍历map j 的元素

    {

        it1=str[i].find(it->first);//到i 中查询

        if(it1==str[i].end())//未找到

        {

            str[i][it->first]=it->second;//加入

            if(it->second==K)//如果正好等于K 则答案加一

            ++(ans[I]);

            continue;

        }

        if(it1->second==K)//如果找到 本来在i中 这个数出现次数正好为K 在加上一个非0数则不等于K 了所以答案个数减1

        --(ans[I]);

        if((it1->second+=it->second)==K)//如果加上正好为K 答案个数加1 这不会和上一个冲突

        ++(ans[I]);

    }

}

int main()

{

    //freopen("data.txt","r",stdin);

    int T;

    scanf("%d",&T);

    for(int cas=1;cas<=T;++cas)

    {

        memset(head,-1,sizeof(head));

        memset(ans,0,sizeof(ans));

        memset(son,0,sizeof(son));//各种初始化

        while(!qu.empty())

        qu.pop();

        int n,q;

        scanf("%d %d",&n,&K);

        for(int i=1;i<=n;++i)

        {

            int a;

            str[i].clear();//清空

            id[i]=i;//本来每个节点 的map 就是自己对应的

            scanf("%d",&a);

            str[i][a]=1;//先都加入 本节点的数

            if(K==1)//如果K正好为1 则答案也更新

            ++ans[i];

        }

        int x,y;

        for(int i=1;i<n;++i)//输入边 建树

        {

            scanf("%d %d",&x,&y);

            side[i].j=y;

            build(x,i);

            side[i+n].j=x;

            build(y,i+n);/

        }

        dfs(1,0);//搜一个

        while(!qu.empty())

        {

            int l=qu.front();//取元素

            if(l==1)

            break;//如果到了1 则全求出 可以停止

            qu.pop();

            int fa=f[l];//fa为父亲节点

            --son[fa];//父亲节点的可以更新的儿子节点减少1

            if(son[fa]==0)//这是最后一个儿子节点 这是将fa加入队列 千万不能第一次更新就加入 

            {//否则会出现父节点在儿子节点前面的情况 就错了(自己输在这里了)  见上面数据

                qu.push(fa);

            }

            int li=id[l];//找到对应的map 

            int fai=id[fa];

            if(str[fai].size()>str[li].size())//比较大小

            {

                Add(fa,fai,li);

            }else

            {

                ans[fa]=ans[l];//如果需要往l上合并 则先等于l的ans 在下面更新后ans[fa] 始终保存当前对应map里面的答案

                id[fa]=li;//更改对应的map

                Add(fa,li,fai);//更新

            }

        }

        scanf("%d",&q);

        printf("Case #%d:\n",cas);

        while(q--)

        {

            scanf("%d",&x);

            printf("%d\n",ans[x]);

        }

        if(cas<T)

        printf("\n");

    }



    return 0;

}

  

你可能感兴趣的:(count)