hdu 3887 树状数组(模拟栈)

一棵树,求每个结点的子树中有几个数是小于这个数的

dfs会进入一个点一次,出一个点一次,中间经过的点都是它的子树中的点,所以,进入的时候统计一遍,出来的时候统计一遍,两个结果相减就可以了

纯dfs会爆栈,要模拟栈。

不过,不模拟栈也可以过,在网上看到c++可以设置栈的大小,所以把栈空间设置的大一点直接dfs做就可以了

View Code
#pragma comment(linker,"/STACK:100000000,100000000")
#include<cstdio>
#include<cstring>
const int maxn = 100010;
struct EDGE{
int v,next;
}edge[maxn*2];
int head[maxn],ans[maxn],p[maxn];
int tot;
void add(int a,int b){
edge[tot].v=a; edge[tot].next=head[b];head[b]=tot++;
edge[tot].v=b; edge[tot].next=head[a];head[a]=tot++;
}
int c[maxn];
int lowbit(int x){
return x&-x;
}
void update(int x,int d){
for(;x<maxn;c[x]+=d,x+=lowbit(x));
}
int sum(int x){
int ans=0;
for(;x>0;ans+=c[x],x-=lowbit(x));
return ans;
}
void dfs(int x,int fa){
p[x]=sum(x-1);
for(int i=head[x];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(v!=fa){
update(v,1);
dfs(v,x);
}
}
ans[x]=sum(x-1)-p[x];
}
int main(){
int n,root,i,j,a,b;
while(scanf("%d%d",&n,&root),(n||root)){
tot=0;
memset(head,-1,sizeof(head));
memset(c,0,sizeof(c));
for(i=0;i<n-1;i++){
scanf("%d%d",&a,&b);
add(a,b);
}
dfs(root,root);
for(i=1;i<n;i++) printf("%d ",ans[i]);
printf("%d\n",ans[n]);
}
return 0;
}


当然,模拟栈是通用做法,一定要学会

View Code
#include<cstdio>
#include<stack>
#include<cstring>
using namespace std;
stack<int> ss;
const int maxn = 100010;
struct EDGE{
int v,next;
}edge[maxn*2];
int head[maxn],ans[maxn],l[maxn],r[maxn],seq[maxn*2],re[maxn],c[maxn*2];
bool vis[maxn];
int tot;
void add(int a,int b){
edge[tot].v=a; edge[tot].next=head[b];head[b]=tot;re[b]=tot++;
edge[tot].v=b; edge[tot].next=head[a];head[a]=tot;re[a]=tot++;
}
int lowbit(int x){
return x&-x;
}
void update(int x,int d){
for(;x<2*maxn;x+=lowbit(x))
c[x]+=d;
}
int sum(int x){
int ans=0;
for(;x>0;ans+=c[x],x-=lowbit(x));
return ans;
}
void dfs(int root){
int cnt=1;
while(!ss.empty()) ss.pop();
memset(vis,false,sizeof(vis));
ss.push(root);
while(!ss.empty())
{
int now=ss.top();
if(!vis[now])
{
seq[cnt]=now;
l[now]=cnt++;
vis[now]=true;
}
bool flag=false;
for(int i=re[now];i!=-1;i=edge[i].next)
{
if(!vis[edge[i].v])
{
flag=true;
ss.push(edge[i].v);
re[now]=edge[i].next;//记录当前点遍历到了第几个邻接点了,返回的时候从这个邻接点开始
break;
}
}
if(flag) continue;
if(vis[now])
{
seq[cnt]=now;
r[now]=cnt++;
ss.pop();
}
}
}
void solve(int n)
{
for(int i=1;i<=n;i++)
{
ans[i]=sum(r[i]-1)-sum(l[i]);
update(l[i],1);
}
}
int main(){
int n,root,i,j,a,b;
while(scanf("%d%d",&n,&root),(n||root)){
tot=0;
memset(head,-1,sizeof(head));
memset(re,-1,sizeof(re));
memset(c,0,sizeof(c));
for(i=0;i<n-1;i++){
scanf("%d%d",&a,&b);
add(a,b);
}
dfs(root);
solve(n);
for(i=1;i<n;i++) printf("%d ",ans[i]);
printf("%d\n",ans[n]);
}
return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值