【2019.10.25 OI-Killer的模拟赛】2.黄队的宫殿

时间:2019-10-25
本文章向大家介绍【2019.10.25 OI-Killer的模拟赛】2.黄队的宫殿,主要包括【2019.10.25 OI-Killer的模拟赛】2.黄队的宫殿使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目链接

题意:

  给出一棵有$n$个节点的树,每个节点有亮/暗的一个属性。$m$次操作,每次给出一个点$x$,对$x$以及与$x$相邻的点取反,或者询问这些点里一共有多少个是亮着的。

  $1\le x\le n \le 10^6$

分析一:

  按BFS序将树写成一个序列,那么每组兄弟节点一定相邻,线段树维护单点修改,单点查询,区间修改,区间查询即可。

实现一(略)

分析二:

  每个点的属性只跟父亲、自己、孩子的修改次数奇偶有关。每次修改影响不会超过两条边的半径。

  考虑一个点$x$仅记录三条信息:

  $l$:$x$的修改异或${son[x]}$的修改

  $f$:$x$的修改

  $s$:${son[x]}$里亮着的个数

  那么一个点$x$的属性可以由$l[x]\oplus f[fa[x]]$得来。“$\oplus$”表示异或。

  每次修改$x$仅操作$x$、$fa[x]$、$fa[fa[x]]$。

实现二(100分):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#define IL inline
#define UI unsigned int
#define RI register int
#define _1 first
#define _2 second
using namespace std;
const int N=1e6;

    int n,m,a[N+3];
    bool l[N+3],f[N+3];
    int sz[N+3],s[N+3];

int main(){
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
        scanf("%d",&a[i]);
    
    memset(sz,0,sizeof sz);
    for(int i=2;i<=n;i++)
        sz[a[i]]++;
    
    memset(l,0,sizeof l);
    memset(f,0,sizeof f);
    memset(s,0,sizeof s);
    scanf("%d\n",&m);
    while(m--){
        int t,x;    scanf("%d%d",&t,&x);
        if(t==1){
            s[x]=sz[x]-s[x];
            l[x]^=1;    f[x]^=1;
            
            if(x==1)
                continue;
            l[a[x]]^=1;
            (l[x]^f[a[x]])?(s[a[x]]++):(s[a[x]]--);
            
            if(a[x]==1)
                continue;
            (l[a[x]]^f[a[a[x]]])?(s[a[a[x]]]++):(s[a[a[x]]]--);
            
        }
        else {
            int s1=(a[x]==1)?l[a[x]]:(l[a[x]]^f[a[a[x]]]);
            int s2=(x==1)?l[x]:(l[x]^f[a[x]]);
            printf("%d\n",s1+s2+s[x]);
            
        }
        
    }

    return 0;

}
View Code

小结:

  记录/修改所有元素的精确值往往很麻烦。可以考虑把从属关系分类合并,用“粗略信息”回答询问。例如本题的$s$,和10.16发的《矩阵》题解里的求和。

原文地址:https://www.cnblogs.com/Hansue/p/11738788.html