Codeforces 877 E Danil and a Part-time Job(线段树+dfs序)

题目地址
题意:给你一棵树,1为根节点,每个节点都有应该状态0或者1,你有两种操作,pow的操作是把该节点以及他的所有子树的每个节点进行自身异或的操作,get就是查询该节点以及他的所有子树的每个节点有多少个1。
思路:我们先通过dfs一边把所有的子树搜索出来,并标记好,然后我们通过这个给出的子树的范围对线段树进行更新,之后的操作就是根据之前求出的子树范围来更新和查询,线段树就是一个简单的区间更新+延迟更新。

#include <iostream>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <cstdio>
#include <algorithm>
#define N 200010
#define LL __int64
#define inf 0x3f3f3f3f
#define lson l,mid,ans<<1
#define rson mid+1,r,ans<<1|1
#define getMid (l+r)>>1
#define movel ans<<1
#define mover ans<<1|1
using namespace std;
const LL mod = 1e9 + 7;
const double eps = 1e-9;
struct node {
    LL sum;
    int lazy;
    int l, r;
}sum[N << 2];//线段树主体
int num[N], pos[N], len[N], pre[N], cnt;
vector<int> v[N];
struct Segment__Tree {
    int x, y;
    void pushUp(int ans) {
        sum[ans].sum = sum[movel].sum + sum[mover].sum;
    }
    void pushDown(int ans) {
        if (sum[ans].lazy) {
            sum[movel].lazy ^= 1;
            sum[mover].lazy ^= 1;
            sum[movel].sum = sum[movel].r - sum[movel].l + 1 - sum[movel].sum;
            sum[mover].sum = sum[mover].r - sum[mover].l + 1 - sum[mover].sum;
            sum[ans].lazy = 0;
        }
    }
    void build(int l, int r, int ans) {
        sum[ans].l = l;
        sum[ans].r = r;
        sum[ans].lazy = 0;
        if (l == r) {
            sum[ans].sum = num[pre[l]];
            return;
        }
        int mid = getMid;
        build(lson);
        build(rson);
        pushUp(ans);
    }
    LL solve(int l, int r, int ans) {
        if (l >= x&&r <= y) {
            return sum[ans].sum;
        }
        pushDown(ans);
        int mid = getMid;
        if (mid<x) {
            return solve(rson);
        }
        else if (mid >= y) {
            return solve(lson);
        }
        else {
            return solve(lson) + solve(rson);
        }
    }
    void updata(int l, int r, int ans) {
        if (l >= x&&r <= y) {
            sum[ans].sum = r - l + 1 - sum[ans].sum;
            sum[ans].lazy ^= 1;
            return;
        }
        pushDown(ans);
        int mid = getMid;
        if (mid<x) {
            updata(rson);
        }
        else if (mid >= y) {
            updata(lson);
        }
        else {
            updata(lson);
            updata(rson);
        }
        pushUp(ans);
    }
}tree;
void init(int n) {
    for (int i = 0; i <= n; i++) {
        v[i].clear();
    }
}
void dfs(int u) {
    int lens = v[u].size();
    for (int i = 0; i < lens; i++) {
        int vv = v[u][i];
        pos[vv] = ++cnt;//子树的起点
        pre[cnt] = vv;
        dfs(vv);
        len[vv] = cnt;//子树的终点
    }
}
int main() {
    cin.sync_with_stdio(false);
    int n, x, q;
    string str;
    while (cin >> n) {
        init(n);
        for (int i = 2; i <= n; i++) {
            cin >> x;
            v[x].push_back(i);
        }
        for (int i = 1; i <= n; i++) {
            cin >> num[i];
        }
        cin >> q;
        cnt = 0;
        pos[1] = ++cnt;
        pre[cnt] = 1;
        len[1] = n;
        dfs(1);
        tree.build(1, n, 1);
        while (q--) {
            cin >> str;
            if (str[0] == 'g') {
                cin >> x;
                tree.x = pos[x];
                tree.y = len[x];
                cout << tree.solve(1, n, 1) << endl;
            }
            else {
                cin >> x;
                tree.x = pos[x];
                tree.y = len[x];
                tree.updata(1, n, 1);
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值