[北大集训2021]经典游戏

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[北大集训2021]经典游戏相关的知识,希望对你有一定的参考价值。

经典游戏

题解

首先,如果不涉及什么换根的骚操作,这显然就是一个 s g sg sg函数的模板题。
在每个点的位置上放棋子的 s g sg sg函数值是它子树内其他点 s g sg sg函数值的 m e x mex mex,由于这些点的 s g sg sg函数值是连续的,所以点 x x x s g sg sg函数值就是 x x x子树的高度。
不同点的 s g sg sg函数值是异或起来的,所以只要这些点的异或和大于整棵树的高度,先手就获胜,否则后手获胜。

但我们在这道题内,要在修改点的同时,维护所有点的 s g sg sg函数值异或和,并且还要维护所有点的合法性,看着有点难办。
我们先想想怎么维护所有点的 s g sg sg函数异或和。
更改点 x x x对点 y y y的贡献,相当于就是以 y y y为根时, x x x子树内到 x x x的最长距离。
显然,这个值最多有两个不同的,也就是最高的子树与次高的子树。
也就是说,在最高的子树内,贡献是次高值,子树外是最高值。
这也就是说,我们按照 d f s dfs dfs序排序后,就可以直接区间进行修改了。
我们对最大子树内异或上次大值,其余异或上最大值,用线段树就能维护每个点的异或和。

但我们的目的是查询一个点和与它相邻的点有多少能做根。
这也就是说,询问与它相邻的点大于该点最深子树的个数。
可以观察到,其实与它相邻的大部分点的最深子树的深度都是相同的,肯定都是到直径的端点的,所以说,一个点的非长儿子,它们对应的最深子树其实都是延伸到长儿子内的。
也就是说,对于一个点,我们只需要查询它的轻儿子中有多少个小于长儿子深度加 1 1 1的,剩下的长儿子、父亲、和它自身我们可以单独算。
上面的查询也就意味着我们得把它们扔到一个以权值为序的数据结构里,还得在修改时维护它们权值的变化。
由于我们修改时异或的形式,所以 T r i e Trie Trie树是相当好的一个选择。
可以发现,一个点的所有轻儿子在修改时进行的变化除了被修改的那个点都是相同的,所以我们可以考虑直接另开一棵树给 T r i e Trie Trie树维护它的全局标记。
查询的时候就把这个标记给拿出来,与标记相同的这边就是 0 0 0,不同的就是 1 1 1
这样就能比较方便地在 T r i e Trie Trie树上计算答案。
至于修改的那个点,就在它的父亲的 T r i e Trie Trie上改改就行了。

时间复杂度 O ( ( n + q ) log ⁡ n ) O\\left((n+q)\\log n\\right) O((n+q)logn)
另外就是经 S i R i e h n _ n x \\rm S\\colorrediRiehn\\_nx SiRiehn_nx提醒,直接动态开点空间会炸,要加上节点回收。

源码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
#define MAXN 1000005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
template<typename _T>
void read(_T &x)
    _T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9')if(s=='-')f=-1;s=getchar();
    while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
    x*=f;

template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
void Add(int &x,int y,int p)x=add(x,y,p);
int qkpow(int a,int s,int p)int t=1;while(s)if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;return t;
int n,m,a[MAXN],head[MAXN],sz[MAXN],tot,ord[MAXN],sum[MAXN],root[MAXN];
int dfn[MAXN],rd[MAXN],idx,hp[MAXN],wson[MAXN],gp[MAXN],father[MAXN];pii mx[MAXN];
struct edgeint from,to,nxt,op;e[MAXN<<1];
void addEdge(int u,int v)e[++tot]=(edge)u,v,head[u];head[u]=tot;
class SegmentTree
    private:
        int tr[MAXN<<2],ip[MAXN],lim;
    public:
        void build(int rt,int l,int r)
            lim=1;while(lim<r-l+1)lim<<=1;
            for(int i=l;i<=r;i++)ip[i]=lim+i-1;
        
        void modify(int l,int r,int aw)
            if(l>r||r<1||l>lim)return ;
            int nowl=ip[l],nowr=ip[r];
            while(nowl<nowr)
                if(nowl&1)tr[nowl]^=aw,nowl++;
                if(nowr+1&1)tr[nowr]^=aw,nowr--;
                nowl>>=1;nowr>>=1;
            
            if(nowl==nowr)tr[nowl]^=aw;
        
        int query(int ai)
            int now=ip[ai],res=0;
            while(now)res^=tr[now],now>>=1;
            return res;
        
T,Tp;
class TrieTree
    private:
        int sum[MAXN*20],ch[MAXN*20][2],sta[MAXN*20],stak;
        int newnode()return stak?sta[stak--]:++tot;
    public:
        void insert(int id,int ai,int aw)
            if(!root[id])root[id]=newnode();int now=root[id];
            sum[now]+=aw;if(!sum[now])sta[++stak]=now,root[id]=0;
            for(int i=19;i>=0;i--)
                int t=(ai>>i)&1;if(!ch[now][t])ch[now][t]=newnode();
                int las=now;now=ch[now][t];sum[now]+=aw;
                if(!sum[now])ch[las][t]=0,sta[++stak]=now;
            
        
        int query(int id,int k,int kp)
            int res=0,now=root[id];k^=kp;
            for(int i=19;i>=0&&now;i--)
                int t=(k>>i)&1,tp=(kp>>i)&1;
                if(t==tp)res+=sum[ch[now][t^1]];
                now=ch[now][t];
            
            return res;
        
S;
void dosaka1(int u,int fa)
    dfn[u]=++idx;ord[idx]=u;father[u]=fa;
    for(int i=head[u];i;i=e[i].nxt)
        int v=e[i].to;if(v==fa)continue;
        dosaka1(v,u);hp[u]=max(hp[v]+1,hp[u]);
        if(hp[v]>=hp[wson[u]])wson[u]=v;
    
    rd[u]=idx;

void dosaka2(int u,int fa)
    for(int i=head[u];i;i=e[i].nxt)
        int v=e[i].to,tmp=gp[v]+1;
        if(tmp>mx[u].fir)mx[u].sec=mx[u].fir,mx[u].fir=tmp;
        else if(tmp>mx[u].sec)mx[u].sec=tmp;
    
    for(int i=head[u];i;i=e[i].nxt)
        int v=e[i].to;if(v==fa)continue;
        if(mx[u].fir==gp[v]+1)gp[u]=mx[u].sec;
        else gp[u]=mx[u].fir;
        dosaka2(v,u);
    

int main()
    //freopen("classic.in","r",stdin);
    //freopen("classic.out","w",stdout);
    int pts;read(pts);
    read(n);read(m);
    for(int i=1;i<n;i++)
        int u,v;read(u);read(v);
        addEdge(u,v);addEdge(v,u);
    
    for(int i=1;i<=n;i++)read(a[i]);
    dosaka1(1,0);for(int i=1;i<=n;i++)gp[i]=hp[i];
    dosaka2(1,0);T.build(1,1,n);Tp.build(1,1,n);
    for(int i=1;i<=n;i++)if(a[i]&1)
        if(wson[i]&&hp[wson[i]]+1==mx[i].fir)
            T.modify(1,n,mx[i].fir),
            T.modify(dfn[wson[i]],rd[wson[i]],mx[i].sec^mx[i].fir);
        else T.modify(1,n,mx[i].sec),
            T.modify(dfn[i],rd[i],mx[i].fir^mx[i].sec);
    
    for(int i=1;i<=n;i++)sum[i]=T.query(dfn[i]);
    for(int i=1;i<=n;i++)
        for(int j=head[i];j;j=e清华集训2016Alice和Bob又在玩游戏

uoj#266. 清华集训2016Alice和Bob又在玩游戏(博弈论)

[BZOJ4730][清华集训2016][UOJ266] Alice和Bob又在玩游戏

UOJ 266 清华集训2016Alice和Bob又在玩游戏

2021 7.5~7.20日集训总结

2021 7.5~7.20日集训总结