hdu 3887 树状数组

给你一棵树,每个节点都有个编号。

让你求一个节点他的子树中编号比他小的节点有几个。(编号唯一,从1-N,已给出根节点)

解:

树状数组统计。转化为线性序列。

         可以想到的是,若要统计一个节点,那么比它小的孩子必须先插完,然后统计就行了。对于一个节点i来说,只要把所有x<i的都给插到L[x]里面,然后统计L[i]到R[i]有多少个1就行了。

         那么对于所有节点来说也是这样的,从一开始插,插一个,查询一个即可。

         完全是自己想的,代码也是自己写的,感觉自己的能力真的在一步一步增强。还自己手写了一个dfs。。。到底STACK OVERFLOW是不是dfs太深的缘故啊。好像是。因为我手写了一个栈就过了。

/*
Pro: 0

Sol:

date:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#define maxn 111111
using namespace std;
//linr ????
int n,p,head[maxn],esub,a,b,li,L[maxn],R[maxn],ans[maxn],c[maxn];
struct Edge{
    int v,nxt;
}edge[maxn << 1];
void add(int u, int v){
    edge[esub].v = v;
    edge[esub].nxt = head[u];
    head[u] = esub ++;
}
void dfs(int rt){
    bool vis[maxn];
    memset(vis,0,sizeof(vis));
    stack <int> ss; bool flag ;
    vis[rt] = true;  ss.push(rt);   L[rt] = ++ li;
    while(!ss.empty()){
        int tmp = ss.top();
        flag = true;
        for(int j = head[tmp]; j != -1; j = edge[j].nxt){
            if(!vis[edge[j].v]){
                ss.push(edge[j].v);
                L[edge[j].v] = ++ li;
                vis[edge[j].v] = true;
                flag = false;
                break;
            }
        }
        if(flag) {R[tmp] = li; ss.pop();}
    }
}
//void dfs(int rt){
//    L[rt] = ++li;
//    for(int j = head[rt]; j != -1 ; j = edge[j].nxt){
//        if(!L[edge[j].v])   dfs(edge[j].v);
//    }
//    R[rt] = li;
//}
void modify(int pos, int val){
    while(pos <= n){
        c[pos] += val;
        pos += (pos & -pos);
    }
}
int getsum(int pos){
    int sum = 0;
    while(pos){
        sum += c[pos];
        pos -= (pos & -pos);
    }return sum;
}
bool cmpx(int a,int b) {return L[a] < L[b];}
int main(){
    while(~scanf("%d%d",&n,&p) && (n || p)){
        if(n == 1)
            {puts("0"); continue;}
        memset(head,-1 ,sizeof(head));
        memset(c,0,sizeof(c));
        memset(L,0,sizeof(L));
        esub = li = 0;
        for(int i = 0; i < n - 1; i ++){
            scanf("%d%d",&a,&b);
            add(a,b);   add(b,a);
        }
        dfs(p);
        //很容易想到的是先插小的,并且是离线处理。
        for(int ii = 1; ii <= n; ii ++){
            ans[ii] = getsum(R[ii]) - getsum(L[ii] - 1);
            modify(L[ii],1);
        }
        for(int ii = 1; ii < n; ii ++)
            printf("%d ",ans[ii]);
        printf("%d\n",ans[n]);
    }
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值