poj 3321 apple tree 欧拉序列

题目大意:

      有一颗树 每个节点最多生长一个苹果,有两种操作

      1.改变某个节点状态(有苹果去掉,没有苹果加上一个)

      2.求某个节点的子树上苹果的个数

直接做肯定会超时 ,如果整棵树是一条链的话,时间复杂度就会很高。

解决方法:

    运用时间戳,dfs遍历一遍树,每个节点分别记录他自己的编号,和他的子节点中编号最大的编号,这样就把求节点子树的问题转化为了求线性区间和问题了。

    然后运用线段树,就能够顺利解决了。

我的代码

#include <cstdio>
#include <vector>
#include <iostream>
#include <cstring>
#define maxn 1000005
using namespace std;
int head[maxn];
struct ED{
  int x;
  int next;
}edge[maxn];
int totll;
int L[maxn],R[maxn];
int vis[maxn],totl;
int tree[maxn*3];
int dfs(int v,int fa){
  vis[v]=++totl;
  L[v]=totl;
  for(int i=head[v];i;i=edge[i].next){
    if (edge[i].x!=fa){
     dfs(edge[i].x,v);
    }
  }
  R[v]=totl;
  return 0;
}
int build (int o,int L,int R){
  if (L==R) tree[o]=1;
  else {
    int M=L+(R-L)/2;
    build (o*2,L,M);
    build (o*2+1,M+1,R);
    tree[o]=tree[o*2+1]+tree[o*2];
  }
  return 0;
}
int yy;
int change(int o,int L,int R){
  if (L==R&&L==yy) {
    if (tree[o]) tree[o]=0;
    else tree[o]=1;
  }
  else {
    int M=L+(R-L)/2;
    if (yy<=M) change(o*2,L,M);
    if (yy>M) change (o*2+1,M+1,R);
    tree[o]=tree[o*2]+tree[o*2+1];
  }
  return 0;
}
int y1,y2,_sum;
int query(int o,int L,int R){
  if (y1<=L&&y2>=R) _sum+=tree[o];
  else {
    int M=L+(R-L)/2;
    if (y1<=M) query(o*2,L,M);
    if (y2>M) query(o*2+1,M+1,R);
  }
  return 0;
}
int main (){
  int n,m;
  while (scanf("%d",&n)!=EOF){
    memset(head,0,sizeof(head));
    memset(edge,0,sizeof(edge));
    totll=0;
    for (int i=0;i<n-1;i++){
      int a,b;
      scanf("%d%d",&a,&b);
      int bbb=head[a];
      head[a]=++totll;
      edge[totll].x=b;
      edge[totll].next=bbb;
      bbb=head[b];
      head[b]=++totll;
      edge[totll].x=a;
      edge[totll].next=bbb;
    }
    totl=0;dfs(1,0);
    build(1,1,n);
    scanf("%d",&m);
    for (int i=0;i<m;i++){
      char c[2];scanf("%s",c);
      int x;scanf("%d",&x);
      if (c[0]=='Q'){
        _sum=0;
        y1=L[x];y2=R[x];
        query(1,1,n);
        cout<<_sum<<endl;
      }
      if (c[0]=='C'){
        yy=vis[x];
        change(1,1,n);
      }
    }
  }
  return 0;
}
注意:不要使用vector类 否则会超时,最好使用邻接表。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值