hdu 3887 Counting Offspring

来源:互联网 发布:手机淘宝怎么快速秒杀 编辑:程序博客网 时间:2024/05/20 13:39
 

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

                         Counting Offspring

                                                                      Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
                                                                                          Total Submission(s): 348    Accepted Submission(s): 111

Problem Description
You are given a tree, it’s root is p, and the node is numbered from 1 to n. Now define f(i) as the number of nodes whose number is less than i in all the succeeding nodes of node i. Now we need to calculate f(i) for any possible i.
 

Input
Multiple cases (no more than 10), for each case:
The first line contains two integers n (0<n<=10^5) and p, representing this tree has n nodes, its root is p.
Following n-1 lines, each line has two integers, representing an edge in this tree.
The input terminates with two zeros.
 

Output
For each test case, output n integer in one line representing f(1), f(2) … f(n), separated by a space.
 

Sample Input
15 77 107 17 97 37 410 1414 214 139 119 66 56 83 153 120 0
 
Sample Output
0 0 0 0 0 1 6 0 3 1 0 0 0 2 0
 
题意:给定一棵树,已知根节点,求f [ i ],f [ i ]为以 i 为根节点的子树的比 i 小的节点个数。
分析:后序遍历树,就可以得到以每个节点为根的子节点所在的区间,如:样例后续遍历得到的区间为:
后序遍历序列:2   13   14   10    1    11    5    8    6    9    12    15     3       4      7
时间戳:          1    2    3     4     5     6     7    8    9    10   11    12    13    14    15
以9为根节点的子节点就会在一段连续的区间内(下划线部分),用[s [ i ] , t  [ i ]]记录以i为根的子节点所在的区间,如9的子节点的区间为[6,10],现在的问题就是求区间[6,10]内比9小的节点的个数,自然想到树状数组。用[1,10]内比9小的个数减去[1,5]内比9小的个数就是答案。所以从做到右扫描时间戳,对于时间i,对于所有的k满足s[k]=i的节点k查询当前比k小的个数,保存在ans1[k]中,并更新树状数组。第二次扫描,对于时间i,对于所有的k满足t[k]=i的节点查询比k小的个数,保存在ans2[k]中,并更新树状数组。最后对于所有的k,f[k]=ans2[k]-ans1[k]。
ps:后序遍历栈模拟,递归容易暴栈
#include<stdio.h>#include<string.h>#define N 100050int t;int n,a[N],b[N],c[N],num[N];int e,head[N],nxt[N+N],pnt[N+N];char vis[N];int ans1[N],ans[N],r[N];void addedge(int u,int v){pnt[e]=v;nxt[e]=head[u];head[u]=e++;pnt[e]=u;nxt[e]=head[v];head[v]=e++;}int s[N],top=0;void dfs(int u) //先序遍历,后序遍历得到的序列就是把先序遍历序列反过来{               //a[u]就是上面所说的t[u],b[u]就是上面所说的s[u]int v,i;s[top++]=u;vis[u]=1;a[u]=t;while(top>0){u=s[top-1];num[u]++;for(i=head[u];i!=-1;i=nxt[i]){v=pnt[i];head[u]=nxt[i];if(vis[v]==0){s[top++]=v;vis[v]=1;t--;a[v]=t;break;}}if(i==-1){top--;b[u]=t;}}}int lowbit(int x){return x&(-x);}void update(int i,int val){while(i<=n){c[i]+=val;i+=lowbit(i);}}int query(int i){int sum=0;while(i>0){sum+=c[i];i-=lowbit(i);}return sum;}int e1,pnt1[N],nxt1[N],head1[N];void addedge1(int u,int v){pnt1[e1]=v;nxt1[e1]=head1[u];head1[u]=e1++;}int main(){int root,i,j,u,v;while(scanf("%d%d",&n,&root)!=EOF){if(n==0&&root==0)break;t=n;e=0;memset(head,-1,sizeof(head));memset(vis,0,sizeof(vis));memset(num,0,sizeof(num));for(i=1;i<n;i++){scanf("%d%d",&u,&v);addedge(u,v);}dfs(root);e1=0;memset(head1,-1,sizeof(head1));for(i=1;i<=n;i++){r[a[i]]=i;addedge1(b[i],i);  //链表插入s[k]=i的所有k}memset(c,0,sizeof(c));for(i=1;i<=n;i++){for(j=head1[i];j!=-1;j=nxt1[j]){v=pnt1[j];ans1[v]=query(v);}update(r[i],1);}memset(c,0,sizeof(c));for(i=1;i<=n;i++){ans[r[i]]=query(r[i])-ans1[r[i]];update(r[i],1);}printf("%d",ans[1]);for(i=2;i<=n;i++)printf(" %d",ans[i]);printf("\n");}return 0;}