codeforces 877 problem E Danil and a Part-time Job 【dfs序 + 线段树区间异或修改】

传送门
//题意: 给定一颗有根(1 )树,树上每一个点起始都有一个值(0或1),然后有两个操作,pow x把以 x为根的子树全部异或1, get x输出以x为根的子树中有多少个1.
//思路: 就是一个最裸的dfs序 + 线段树异或修改的,细节请看代码. 注意就是求一个区间中的1,那么就是区间长度-此时区间的值, 然后就是修改下lazy标记处就是了. 还有就是线段树中是dfs序的一些线段,所以在build过程中我们还需要知道此时的线段在树中对应的哪一个点,这样才能初始化.

AC Code

const int maxn = 2e5+5;
int cas=1;
int a[maxn];
int cnt,head[maxn];
int p1[maxn],p2[maxn];
int pre[maxn];
struct node
{
    int to,next;
}e[maxn<<1];
void add(int u,int v)
{
    e[cnt] = (node){v,head[u]};
    head[u] = cnt++;
}
int ti;
void dfs_id(int u,int fa)
{
    p1[u] = ++ti;
    pre[ti] = u;
    for(int i=head[u]; ~i ; i = e[i].next){
        int to = e[i].to;
        if(fa == to) continue;
        dfs_id(to,u);
    }
    p2[u] = ti;
} //p1存的是第一次遇到时序号,p2是第二次遇到的序号.
//那么结点u及其子树存在的连续区间就是p1[u] - p2[u].
struct Tree{
    int tl, tr, sum ,lazy;
    void fun(int tot) {
        lazy ^= 1;
        sum = tot - sum;
    }
} tree[maxn<<2];

void pushup(int id)
{
    tree[id].sum = tree[id<<1].sum+tree[id<<1|1].sum;
}

void pushdown(int id,int l,int r)
{
    if(tree[id].lazy){
        int mid = (l+r)>>1;
        tree[id<<1].fun(mid-l+1);
        tree[id<<1|1].fun(r-mid);
        tree[id].lazy = 0;
    }
}

void build(int id,int l,int r)
{
    tree[id].tl = l; tree[id].tr = r; tree[id].lazy = 0;
    if(l == r){
        tree[id].sum = a[pre[l]];
        return ;
    }
    int mid = (l+r) >> 1;
    build(id<<1,l,mid);
    build(id<<1|1,mid+1,r);
    pushup(id);
}

void update(int id,int st,int ed)
{
    int l=tree[id].tl, r=tree[id].tr;
    if(st <= l && r <= ed){
        tree[id].fun(r-l+1);
        return ;
    }
    pushdown(id,l,r);
    int mid = (l+r) >> 1;
    if(st <= mid) update(id<<1,st,ed);
    if(ed > mid) update(id<<1|1,st,ed);
    pushup(id);
}
int ans;
void query(int id,int ql,int qr)
{
    int l = tree[id].tl , r = tree[id].tr;
    if(ql <= l && r <= qr) {
        ans += tree[id].sum;
        return ;
    }
    pushdown(id,l,r);
    int mid = (l+r) >> 1;
    if(ql <= mid) query(id<<1,ql,qr);
    if(qr > mid) query(id<<1|1,ql,qr);
}

void solve()
{
    int n; scanf("%d",&n);
    ti = 0; Fill(pre,0);
    cnt = 0; Fill(head,-1);
    for(int i=2;i<=n;i++){
        int u;
        scanf("%d",&u);
        add(i,u); add(u,i);
    }
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
    }
    dfs_id(1,-1);
    build(1,1,n);
    int q; scanf("%d",&q);
    while(q--){
        string s; cin >> s;
        int u; scanf("%d",&u);
        if(s == "get"){
            ans = 0;
            query(1,p1[u],p2[u]);
            printf("%d\n",ans);
        }
        else{
            update(1,p1[u],p2[u]);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值