Codevs1228苹果树

http://codevs.cn/problem/1228/

思路
这个题咋一看就会想到树链剖分,但事实上,我们只需要将树链剖分中,由DFS序(当然这里的DFS序与树剖的不同,树剖的是先重链再轻边的DFS序,这里是纯粹的遍历顺序)确定树上节点在数据结构中的对应位置的方法提取出来。用每个点的DFS序,及以此点为根节点的子树中最大的DFS序确定每个子树的范围,用数据结构维护其区间和即可。
我们需要知道:
DFS序主要用于处理子树问题。
而树链剖分则主要用于处理树上两点间问题。

线段树代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define RI register int
using namespace std;
int n,m,ru,rv,tot,pos;
int limt;//当前最大dfs序 
int first[200010],nxt[200010];
int seq[100010];//每个点的dfs序 
char ch;
struct edge
{
    int u,v;
}l[200010];
struct inte
{
    int l,r,A;
}tree[400010];
struct point
{
    int ls,rs;//每个点dfs序,以此点为根的子树中最大的dfs序 
}p[100010];//以每个点为根的子树在seg上的范围 
void add(int f,int t)
{
    l[++tot]=(edge){f,t};
    nxt[tot]=first[f];
    first[f]=tot;
}
void dfs(int k,int fa)
{
    seq[k]=++tot;
    limt=tot;
    for(RI i=first[k];i!=-1;i=nxt[i])
    {
        int x=l[i].v;
        if(x!=fa)
        dfs(x,k);
    }
    p[seq[k]].ls=seq[k];
    p[seq[k]].rs=limt;
}
void update(int now)
{
    tree[now].A=tree[now<<1].A+tree[now<<1|1].A;
}
void build(int now,int l,int r)
{
    tree[now].l=l;
    tree[now].r=r;
    if(l==r)
    {
        tree[now].A=1;
        return;
    }
    int mid=(l+r)>>1;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    update(now);
}
void point_change(int now,int pos)
{
    if(tree[now].l==tree[now].r)
    {
        tree[now].A=tree[now].A^1;
        return;
    }
    int mid=(tree[now].l+tree[now].r)>>1;
    if(pos<=mid)
    point_change(now<<1,pos);
    if(pos>mid)
    point_change(now<<1|1,pos);
    update(now);
}
int ask_sum(int now,int l,int r)
{
    if(tree[now].l>=l&&tree[now].r<=r)
    {
        return tree[now].A;
    }
    int ans=0;
    int mid=(tree[now].l+tree[now].r)>>1;
    if(l<=mid)
    ans+=ask_sum(now<<1,l,r);
    if(r>mid)
    ans+=ask_sum(now<<1|1,l,r);
    return ans;
}
int main()
{
    memset(first,-1,sizeof(first));
    scanf("%d",&n);
    for(RI i=1;i<=n-1;i++)//别再错了这是颗n-1条边 
    {
        scanf("%d%d",&ru,&rv);
        add(ru,rv);
        add(rv,ru);
    }
    tot=0; 
    dfs(1,0);
    build(1,1,n);
    scanf("%d",&m);
    for(RI i=1;i<=m;i++)
    {
        ch=getchar();
        while(ch!='C'&&ch!='Q')
        ch=getchar();
        scanf("%d",&pos);
        if(ch=='C')
        {
            point_change(1,seq[pos]);//线段树上的对应位置为其dfs序 
        }
        if(ch=='Q')
        {
            printf("%d\n",ask_sum(1,p[seq[pos]].ls,p[seq[pos]].rs));
        }
    }
    return 0;
}

树状数组代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define RI register int
using namespace std;
int n,m,ru,rv,tot,pos;
int limt;//当前最大dfs序 
int first[200010],nxt[200010];
int seq[100010];//每个点的dfs序 
int c[100010];
char ch;
struct edge
{
    int u,v;
}l[200010];
struct point
{
    int ls,rs;//每个点dfs序,以此点为根的子树中最大的dfs序 
}p[100010];//以每个点为根的子树在BIT上的范围 
int lowbit(int x)
{
    return x&(-x);
}
void build(int f,int t)
{
    l[++tot]=(edge){f,t};
    nxt[tot]=first[f];
    first[f]=tot;
}
void dfs(int k,int fa)
{
    seq[k]=++tot;
    limt=tot;
    for(RI i=first[k];i!=-1;i=nxt[i])
    {
        int x=l[i].v;
        if(x!=fa)
        dfs(x,k);
    }
    p[seq[k]].ls=seq[k];
    p[seq[k]].rs=limt;
}
void add(int k,int value)
{
    for(int i=k;i<=n;i+=lowbit(i))
    c[i]+=value;
}
int ask_sum(int l,int r)
{
    int ans=0;
    for(int i=r;i>0;i-=lowbit(i))
    ans+=c[i];
    for(int i=l-1;i>0;i-=lowbit(i))//注意边界 
    ans-=c[i];
    return ans;
}
int main()
{
    memset(first,-1,sizeof(first));
    scanf("%d",&n);
    for(RI i=1;i<=n-1;i++)
    {
        scanf("%d%d",&ru,&rv);
        build(ru,rv);
        build(rv,ru);
    }
    tot=0; 
    dfs(1,0);
    for(RI i=1;i<=n;i++)
    add(i,1);//建立树状数组 
    scanf("%d",&m);
    for(RI i=1;i<=m;i++)
    {
        ch=getchar();
        while(ch!='C'&&ch!='Q')
        ch=getchar();
        scanf("%d",&pos);
        if(ch=='C')
        {
            if((c[seq[pos]]-ask_sum(seq[pos]-lowbit(seq[pos])+1,seq[pos]-1))==1)//单点查询   
            //左子树及自身的值之和-ask_sum(左子树第一个元素,左子树最后一个元素)
            //或者写if(ask_sum(seq[pos],seq[pos])==1)
            add(seq[pos],-1);
            else add(seq[pos],1);
        }
        if(ch=='Q')
        {
            printf("%d\n",ask_sum(p[seq[pos]].ls,p[seq[pos]].rs));
        }
    }
    return 0;
}

话说我要是再写一个分块版本好像就可以凑一个 >形形色色的苹果树< ….
不过由于本题比较简单就不再赘述分块版本了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值