hdu3887 树状数组--思维还是有待提高啊。。

http://acm.hdu.edu.cn/showproblem.php?pid=3887
来源:

2011 Multi-University Training Contest 5 - Host by BNU 

题意:给定一颗10^5个结点的树(用1--n表示),告诉你根节点,求每个结点下面序号比它小的个数。。。

分析:其实就是利用的逆序数求法的思想。。。深搜这棵树,树状数组维护,对每个结点,第一次进入它时记录当前扫描过得结点比它小的个数,在退出该结点时再次记录其个数,两者相减就是结果。。。比赛的时候始终在想如何去掉前面多余的部分,居然想不到进入该结点的时候记录一下就好了。。。郁闷。。。
本题直接DFS爆栈,所以要模拟一下。。。

先贴代码:

#include <vector>
#include <iostream>
using namespace std;

const int N=200010;
int n, p, sum[N], ans[N], stk[N], sn;
int pre[N], pos[N], flag[N];
vector<int> a[N];

int query(int i)
{
    int tmp = 0;
    while(i>0)
    {
        tmp += sum[i];
        i -= i&(-i);
    }
    return tmp;
}
void update(int i, int v)
{
    while(i<=n)
    {
        sum[i] += v;
        i += i&(-i);
    }
}

void dfs(int p) //栈模拟。。。
{
    int ii;
    sn = 0;
    stk[sn++] = p;
	memset(flag, 0, sizeof(flag));
    while(sn!=0)
    {
        ii = stk[sn-1];
		if(flag[ii]==0)
		{
			pre[ii] = query(ii);
			flag[ii] = 1;
		}
        if(a[ii].size()!=0)
		{
			if(flag[a[ii].back()]==0)
				stk[sn++] = a[ii].back();
			a[ii].pop_back();
		}
		else
		{
			pos[ii] = query(ii);
			ans[ii] = pos[ii] - pre[ii];
			update(ii, 1);
			sn--;
		}
    }
}

int main()
{
    int i, x, y;
    while(scanf("%d%d", &n, &p)!=EOF)
    {
		if(n==0 && p==0)
			break;
        for(i=0; i<=n; i++)
        {
            sum[i] = 0;
            ans[i] = 0;
        }
        for(i=1; i<n; i++)
        {
            scanf("%d%d", &x, &y);
            a[x].push_back(y);
            a[y].push_back(x);
        }
        dfs(p);
        for(i=1; i<n; i++)
            printf("%d ", ans[i]);
		printf("%d\n", ans[i]);
    }
    return 0;
}


你可能感兴趣的:(hdu3887 树状数组--思维还是有待提高啊。。)