BZOJ4134 : ljw和lzr的hack比赛
设f[x]为x子树里的子游戏的sg值,h[x]为x所有儿子节点f[x]的异或和,则:
f[x]=mex(y到x路径上所有点的h的异或和 xor y到x路径上所有点的f的异或和),y是x子树中的一个白点。
考虑一个白点对其祖先的影响,可以发现每往上走一步,一个子树里的贡献将会异或上一个相同的数。
用Trie来维护每个点子树里所有白点的贡献,需要支持合并操作、mex的查询,以及一个异或的标记,下传时交换左右儿子。
时间复杂度O(nlogn)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | #include<cstdio> const int N=100010,M=4000000; int n,m,i,x,y,a[N],g[N],v[N<<1],nxt[N<<1],ed,f[N],h[N]; int l[M],r[M],tag[M],T[N],tot; bool val[M],fin[N]; inline void read( int &a){ char c; while (!(((c= getchar ())>= '0' )&&(c<= '9' )));a=c- '0' ; while (((c= getchar ())>= '0' )&&(c<= '9' ))(a*=10)+=c- '0' ;} inline void add( int x, int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} inline void rev( int d, int p, int x){ if (!x||d<0) return ; tag[x]^=p; if (p>>d&1){ int t=l[x];l[x]=r[x];r[x]=t;} } inline void pb( int d, int x){ if (tag[x])rev(d-1,tag[x],l[x]),rev(d-1,tag[x],r[x]),tag[x]=0;} int build( int d, int p){ int x=++tot; if (d<0) return val[x]=1,x; if (p>>d&1)r[x]=build(d-1,p); else l[x]=build(d-1,p); return x; } int merge( int d, int p, int x, int y){ if (!y) return x; if (!x){ rev(d,p,y); return y; } int z=++tot; if (d<0) return val[z]=1,z; pb(d,x),pb(d,y); if (p>>d&1)l[z]=merge(d-1,p,l[x],r[y]),r[z]=merge(d-1,p,r[x],l[y]); else l[z]=merge(d-1,p,l[x],l[y]),r[z]=merge(d-1,p,r[x],r[y]); return val[z]=val[l[z]]&val[r[z]],z; } inline int mex( int x){ int t=0; for ( int i=m;~i;i--){ pb(i,x); if (!val[l[x]])x=l[x]; else x=r[x],t|=1<<i; } return t; } void dfs( int x, int y){ for ( int i=g[x];i;i=nxt[i]) if (v[i]!=y)dfs(v[i],x),h[x]^=f[v[i]]; if (!a[x])T[x]=build(m,h[x]); for ( int i=g[x];i;i=nxt[i]) if (v[i]!=y)T[x]=merge(m,h[x]^f[v[i]],T[x],T[v[i]]); f[x]=mex(T[x]); } void cal( int x, int y, int z){ z^=f[x]^h[x]; if (!a[x]&&!z)fin[x]=1; for ( int i=g[x];i;i=nxt[i]) if (v[i]!=y)cal(v[i],x,z); } int main(){ for (read(n),i=1;i<=n;i++)read(a[i]); for (i=1;i<n;i++)read(x),read(y),add(x,y),add(y,x); while ((1<<m)<=n)m++;m--; dfs(1,0); if (!f[1]) return puts ( "-1" ),0; for (cal(i=1,0,f[1]);i<=n;i++) if (fin[i]) printf ( "%d\n" ,i); return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术