2 条题解

  • 1
    @ 2026-9-30 15:46:28

    暴力

    测试点 1,2,31, 2, 3 满足 n,m≤103n,m\le10^3。
    可以使用 O(N2)O(N^2) 的算法。
    故,opt为 00 时,遍历 xx 的字数并修改权值。
    opt为 11 时,遍历 xx 的字数并记录权值为 11 的点的个数。

    优化

    发现性质:对于一个子数大小为 sizxsiz_x 的节点 xx,他的子树中权值为 11 的点的个数是 cntxcnt_x。取反其子树时,cntcnt 会更新为 sizx−cntxsiz_x-cnt_x。于是维护一个 tag 数组,表示他是否翻转。每次翻转操作时把 tagxtag_x 取反(1->0或0->1)。查询时 tagxtag_x 是 11 ,

    虽然我们的修改变成了 O(1)O(1) ,但查询还是 O(n)O(n)。可以使用 dfn序在树上装一个线段树来维护权值为 11 的点的个数。tag数组变为线段数上每一段的翻转懒标记即可。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=1e5+5;
    
    int n,m;
    int a[N],dfn[N],siz[N],b[N],idx;
    vector<int> g[N];
    
    void dfs(int u,int fa){
        dfn[u]=++idx;
        siz[u]=1;
        for(auto v:g[u]){
            if(v==fa) continue;
            dfs(v,u);
            siz[u]+=siz[v];
        }
    }
    
    struct SegTree{
        #define lc (p<<1)
        #define rc (p<<1|1)
        #define mid (l+r>>1)
    
        int n;
        int tag[N<<2],sum[N<<2],cnt[N<<2];
        SegTree(int n_){
            n=n_;
            memset(tag,0,sizeof tag);
            memset(sum,0,sizeof sum);
            memset(cnt,0,sizeof cnt);
        }
    
        void build(int p,int l,int r){
            cnt[p]=r-l+1;
            if(l==r){
                sum[p]=b[l];
                return;
            }
            build(lc,l,mid);
            build(rc,mid+1,r);
            sum[p]=sum[lc]+sum[rc];
        }
    
        void up(int p){
            sum[p]=sum[lc]+sum[rc];
        }
    
        void down(int p){
            if(!tag[p]) return;
            tag[lc]^=1,tag[rc]^=1;
            sum[lc]=cnt[lc]-sum[lc];
            sum[rc]=cnt[rc]-sum[rc];
            tag[p]=0;
        }
    
        void update(int p,int l,int r,int ql,int qr){
            if(ql<=l&&r<=qr){
                tag[p]^=1;
                sum[p]=cnt[p]-sum[p];
                return;
            }
            down(p);
            if(ql<=mid) update(lc,l,mid,ql,qr);
            if(qr>mid) update(rc,mid+1,r,ql,qr);
            up(p);
        }
    
        int query(int p,int l,int r,int ql,int qr){
            if(ql<=l&&r<=qr) return sum[p];
            down(p);
            int res=0;
            if(ql<=mid) res+=query(lc,l,mid,ql,qr);
            if(qr>mid) res+=query(rc,mid+1,r,ql,qr);
            return res;
        }
    };
    
    int main(){
        cin>>n>>m;
        string s;cin>>s;
        for(int i=1;i<=n;i++) a[i]=s[i-1]-'0';
        for(int i=1;i<n;i++){
            int u,v;cin>>u>>v;
            g[u].push_back(v);
            g[v].push_back(u);
        }
    
        dfs(1,0);
    
        for(int i=1;i<=n;i++) b[dfn[i]]=a[i];
    
        SegTree seg(n);
        seg.build(1,1,n);
    
        while(m--){
            int opt,x;
            cin>>opt>>x;
            int l=dfn[x],r=dfn[x]+siz[x]-1;
            if(opt==0) seg.update(1,1,n,l,r);
            else cout<<seg.query(1,1,n,l,r)<<'\n';
        }
        return 0;
    }
    
    • 0
      @ 2026-9-30 11:12:43

      通过 dfs 序将子树映射到区间,然后写一个支持区间修改、区间查询的线段树维护区间内的 1 的数量。

      • 1

      信息

      ID
      2764
      时间
      1000ms
      内存
      128MiB
      难度
      6
      标签
      (无)
      递交数
      22
      已通过
      12
      上传者