【学习笔记】「北大集训 2021」经典游戏

我觉得很厉害。要是考场上能把这道题切了的话数据结构的水平肯定是不低的。

考虑简化版问题:如果只询问一个点的答案怎么做。

注意,我这么做是有风险的。我把战线拉长了。不过当然,如果连简化版的问题都做不了,那何谈正解?幸运的是,这确实是一道数据结构多合一的题。

考虑 长链剖分 。那么在 x x x节点上加入棋子时,子树外的点就异或上 x x x子树的最大深度,如果以 x x x为根的最长链在重儿子上面,那么就给除了重儿子外的子树打标记,注意到 dfn \text{dfn} dfn序是连续的 ,可以直接打标;对于重儿子也可以直接对整颗树打标,只需处理出 x x x去掉重儿子后的最长链长度即可;如果最长链在父亲上,那么直接对 x x x整颗子树修改即可。

发现了吗?经过细致分析,我们发现这道题其实并不复杂。当然要建立在想到长链剖分的基础上。

这题更神奇的地方在于,让我们求 dist(x,v) ≤ 1 \text{dist(x,v)}\le 1 dist(x,v)1的所有根的答案。这是个非常恼人的限制,因为你知道会被菊花图卡,但是不知道会被卡成多少分。

有没有严格的做法呢?答案是有的。但是我一定想不到。 但是需要用到非常高级的技巧。其实说白了,询问可以拆分成 x x x x x x的父亲, x x x的重儿子以及 x x x的所有轻儿子。如果一次插入影响到点的数目是 O ( 1 ) O(1) O(1)那么我们的目的就达到了。

考虑这样一个问题,如何用字典树维护 c i ⊕ x > d i c_i\oplus x>d_i cix>di的所有点对?这个问题也非常具有迷惑性。因为 d i d_i di是定值, x x x又是每次询问给定的,那么维护 c i c_i ci然后在 trie \text{trie} trie树上查不就完了?并且 trie \text{trie} trie树查询的复杂度也是 log ⁡ n \log n logn的。这样分析下来觉得越来越有道理了,但是考场上完全想不到这里来啊???

初始化的时候 c i c_i ci都是定值。手动分讨一波,如果最长链在重儿子上面那么 x x x的所有轻儿子都异或上同一个数,直接在 x x x上打标就完了;如果最长链在父亲上面那么轻儿子还是异或上同一个数。事实上可以发现,任意时刻轻儿子的标都和这个点上的标是一样的,唯一的例外是当插入的点就是这个轻儿子的情况。但是正如前所说,修改影响到的节点数目是 O ( 1 ) O(1) O(1)的,所以直接在 trie \text{trie} trie树上暴力修改就行。然后就做完了。

所以发现了吗?这种题逻辑链条太长了。其实思维难度并不大。

复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

要考虑的细节挺多了。所以代码先咕了。

代码写错了好多地方,所以调了好久,看来我还是太菜了。。。

#include<bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back using namespace std; const int N=1e6+5; int type; int n,m,sz[N],dp[N],dp2[N],dpfa[N],son[N],bitxor[N],dfn[N],num; int trie[N*25][2],tot,rt[N],fa[N],sztree[N*25],c[N]; vector<int>G[N]; void dfs(int u,int topf){ sz[u]=1,fa[u]=topf; for(auto v:G[u]){ if(v!=topf){ dfs(v,u),sz[u]+=sz[v]; if(!son[u]||dp[v]>dp[son[u]]){ son[u]=v; } if(dp[v]+1>dp[u])dp2[u]=dp[u],dp[u]=dp[v]+1; else if(dp[v]+1>dp2[u])dp2[u]=dp[v]+1; } } } //fixed void ins(int it,int val,int f){ for(int i=20;i>=0;i--){ int p=val>>i&1; if(!trie[it][p])trie[it][p]=++tot; it=trie[it][p]; sztree[it]+=f; } } int query(int it,int x,int y){ int tot=0; for(int i=20;i>=0;i--){ if(y>>i&1){ it=trie[it][(x>>i&1)^1]; } else { tot+=sztree[trie[it][(x>>i&1)^1]]; it=trie[it][x>>i&1]; } } return tot; } //fixed void dfs2(int u){ dfn[u]=++num; if(son[u])dfs2(son[u]); rt[u]=++tot; for(auto v:G[u]){ if(!dfn[v]){ dfs2(v); ins(rt[u],0,1); } } } void dfs3(int u,int topf){ for(auto v:G[u]){ if(v!=topf){ if(dp[v]+1==dp[u]){ dpfa[v]=dp2[u]+1; } else{ dpfa[v]=dp[u]+1; } if(dpfa[v]>dp[v])dp2[v]=dp[v],dp[v]=dpfa[v]; else if(dpfa[v]>dp2[v])dp2[v]=dpfa[v]; dfs3(v,u); } } } //fixed void add(int x,int y){ assert(x); for(;x<=n;x+=x&-x)bitxor[x]^=y; } void addseq(int l,int r,int x){ assert(l<=r); add(l,x),add(r+1,x); } //fixed int getval(int x){ int tot=0; for(x=dfn[x];x;x-=x&-x)tot^=bitxor[x]; return tot; } int solve(int x){ return x&&getval(x)>dp[x]; } void update(int x){ //fixed if(son[x]&&dp[x]!=dpfa[x]){ addseq(1,n,dp[x]); addseq(dfn[son[x]],dfn[son[x]]+sz[son[x]]-1,dp[x]^dp2[x]); } else{ addseq(1,n,dp2[x]); addseq(dfn[x],dfn[x]+sz[x]-1,dp2[x]^dp[x]); if(fa[x]&&son[fa[x]]!=x){ ins(rt[fa[x]],c[x],-1); c[x]^=dp[x]^dp2[x]; ins(rt[fa[x]],c[x],1); } } } int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>type>>n>>m; //fixed for(int i=1;i<n;i++){ int u,v;cin>>u>>v; G[u].pb(v),G[v].pb(u); } dfs(1,0),dfs2(1); dfs3(1,0); for(int i=1;i<=n;i++){ int x;cin>>x; if(x&1)update(i); } for(int i=1;i<=m;i++){ int x,y;cin>>x>>y; update(x); int res=solve(y)+solve(son[y])+solve(fa[y])+query(rt[y],getval(y),dp[y]+1); cout<<res<<"\n"; } }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17529978.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(14)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2022-05-04 [ICPC2014 WF] Baggage
2022-05-04 [CF858F] Wizard‘s Tour
2022-05-04 [IOI2019] 景点划分
2022-05-04 [CQOI2010] 鼹鼠
点击右上角即可分享
微信分享提示