Codeforces Round #442 (Div. 2) E. Danil and a Part-time Job (dfs序+线段树)

题意:

给你一颗有n个节点的树(根为节点1),每个节点有两种状态,点亮或者没点亮,现在给你每个节点的初始状态和它的父节点,然后进行q次操作,操作有两种,一种是查询某个几点下的子树有多少个亮着的节点,另一种是修改某个节点下的子树的所有节点的状态(就是没点亮变为点亮,点亮变为没点亮)

思路:

首先连边,然后对这棵树进行dfs,并且每个节点都会有一个dfs序的编号,并且由于是dfs序,那么他的子节点的dfs序都应该是和它相连的,并且我们可以把他的子节点的最大dfs序记录下来。根据这个dfs序建立线段树,那么当我们询问某个节点下子树亮着的节点的个数,就能看作是一个RMQ的查询操作,同样的,修改某个节点和其子节点的状态,就可以用lazy去写了

错误及反思:

一开始误以为成了并查集。。。果然还是太年轻了

代码:

#include<bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int maxn= 200000+100;
int last[maxn],sit[maxn],a[maxn],num[maxn];
int segtree[maxn*4],lazy[maxn*4];
vector<int> v[maxn];
int n,now=0;
void dfs(int node)
{
    int q=now;
    for(int i=0;i<v[node].size();i++)
    {
        now++;
        dfs(v[node][i]);
    }
    num[node]=q;
    a[q]=sit[node];
    last[q]=now;
}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        segtree[rt]=a[l];
        return ;
    }
    int m=(l+r)/2;
    build(lson);
    build(rson);
    segtree[rt]=segtree[rt<<1]+segtree[rt<<1|1];
    return ;
}
void pushdown(int l,int r,int rt)
{
    int m=(l+r)/2;
    if(lazy[rt])
    {
        lazy[rt<<1]++; lazy[rt<<1]%=2;
        lazy[rt<<1|1]++; lazy[rt<<1|1]%=2;
        segtree[rt<<1]=m-l+1-segtree[rt<<1];
        segtree[rt<<1|1]=r-m-segtree[rt<<1|1];
        lazy[rt]=0;
    }
}

void update(int L,int R,int l,int r,int rt)
{
    if(L<=l&&R>=r)
    {
        lazy[rt]++;
        lazy[rt]%=2;
        segtree[rt]=r-l+1-segtree[rt];
        return ;
    }
    pushdown(l,r,rt);
    int m=(l+r)/2;
    if(m>=L) update(L,R,lson);
    if(m<R) update(L,R,rson);
    segtree[rt]=segtree[rt<<1]+segtree[rt<<1|1];
    return ;
}
int query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&R>=r)
    {
        return segtree[rt];
    }
    int ans=0;
    pushdown(l,r,rt);
    int m=(l+r)/2;
    if(m>=L) ans+=query(L,R,lson);
    if(m<R) ans+=query(L,R,rson);
    return ans;
}
int main(){
    scanf("%d",&n);
    for(int i=0,temp;i<n-1;i++)
    {
        scanf("%d",&temp);
        v[temp].push_back(i+2);
    }
    for(int i=0,temp;i<n;i++)
    {
        scanf("%d",&temp);
        sit[i+1]=temp;
    }
    dfs(1);

    build(0,n-1,1);
    int m; scanf("%d",&m);
    while(m--)
    {
        char w[10]; int e;
        scanf("%s %d",w,&e);
        if(w[0]=='g'){
            e=num[e];
            int L=e;
            int R=last[e];
            printf("%d\n",query(L,R,0,n-1,1));
        }
        else{
            e=num[e];
            int L=e;
            int R=last[e];
            update(L,R,0,n-1,1);
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值