对这棵树DFS遍历一遍,同一节点入栈和出栈之间访问的节点就是这个节点的子树。
因此节点入栈时求一次 小于 i 的节点个数 和,出栈时求一次 小于 i 的节点个数 和,两次之差就是答案。
PS.这题直接DFS会爆栈,可以重新设置栈的大小
#pragma comment(linker,"/STACK:100000000,100000000")
也可以人工模拟栈,代码如下。
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using namespace std; const int MAXN = 110000; struct Edge { int v; int next; }; int N, root; int EdgeN, top; Edge D[ (MAXN << 2) + 100 ]; int C[ MAXN ]; //树状数组 int ans[ MAXN ]; //答案 int pre[ MAXN ]; //入点前的和 int head[ MAXN ]; int stack[MAXN]; //手工栈 bool vis[MAXN]; int lowbit( int x ) { return x&(-x); } int query( int x ) { int res = 0; while ( x > 0 ) { res += C[x]; x -= lowbit(x); } return res; } void update( int x, int val ) { while ( x <= N ) { C[x] += val; x += lowbit(x); } return; } void AddEdge( int u, int v ) { D[EdgeN].v = v; D[EdgeN].next = head[u]; head[u] = EdgeN++; return; } void showStack( int top ) { for ( int i = 1; i <= top; ++i ) printf( "%d ", stack[i] ); puts("\n==================="); return; } void DFS( int u ) { stack[++top] = u; vis[u] = true; while ( top ) { int uu = stack[top]; if ( !vis[uu] ) pre[uu] = query(uu - 1); vis[uu] = true; int i; for ( i = head[uu]; i != -1; i = D[i].next ) { if ( !vis[ D[i].v ] ) { update( D[i].v, 1 ); stack[++top] = D[i].v; //showStack(top); break; } } if ( i == -1 ) { ans[uu] = query( uu - 1 ) - pre[uu]; --top; } } return; } int main() { //freopen( "s.txt", "w", stdout ); while ( scanf( "%d%d", &N, &root ), N || root ) { EdgeN = 1; memset( head, -1, sizeof(head) ); memset( C, 0, sizeof(C) ); for ( int i = 1; i < N; ++i ) { int u, v; scanf( "%d%d", &u, &v ); AddEdge( u, v ); AddEdge( v, u ); } top = 0; memset( vis, false, sizeof(vis) ); memset( pre, 0, sizeof(pre) ); DFS( root ); printf( "%d", ans[1] ); for ( int i = 2; i <= N; ++i ) printf( " %d", ans[i] ); puts(""); } return 0; }