1. 程式人生 > 實用技巧 >2020 計蒜之道預賽 2D

2020 計蒜之道預賽 2D

先求出bfs序, 同深度的點都在連續的一段區間, 考慮每段區間, 對於一個點$u$, 求出$u$之前最接近的點權為$w_u \space xor \space x$的點$v$, 那麼對$lca(u,v)$到根的路徑有貢獻, 一種深度總貢獻所有這樣的路徑求並集

求並集可以用樹上差分實現, 每條鏈按下端點$dfs$序排序, 下端點權值+1, 相鄰鏈$lca$權值-1

對於排序和求$lca$, 都可以離線$O(n)$實現

對於排序, 由於$dfs$序是$[1,n]$的, 可以全插到一個數組裡, 記錄是哪個詢問, 最後再插回來即可

對於$lca$可以用tarjan求

總複雜度就為$O(n)$

#include <stdio.h>
#include 
<ctype.h> const int P = 998244353; char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf; #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) inline int rd() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) x=x*10+(ch^48),ch=getchar(); return x*f; } namespace Hash { const int hashmod = 1635947; int clk, v[hashmod], vis[hashmod]; unsigned h[hashmod]; int &get(unsigned S) { int i = S%hashmod; for (; vis[i]==clk && h[i] != S; i = (i + 1) % hashmod);
if (vis[i]!=clk) h[i] = S, vis[i] = clk, v[i] = 0; return v[i]; } } const int N = 1e6+10; int n, X, w[N], fa[N], dfn[N], dep[N]; int L[N], val[N], s[N], vis[N], ID[N], q[N]; int h[N],g[N],f2[N],vv[N<<2],nxt[N<<2],ed; int f[N],v[N],to[N<<2],id[N<<2],nxt2[N<<2],ed2; inline void add(int x, int y) {vv[++ed]=y;nxt[ed]=g[x];g[x]=ed;} inline void add2(int x,int y) {vv[++ed]=y;nxt[ed]=h[x];h[x]=ed;} inline void add3(int x, int y) {vv[++ed]=y;nxt[ed]=f2[x];f2[x]=ed;} inline void add4(int x, int y, int z) {to[++ed2]=y;id[ed2]=z;nxt2[ed2]=f[x];f[x]=ed2;} inline void add5(int x, int y, int z) {to[++ed2]=y;id[ed2]=z;nxt2[ed2]=v[x];v[x]=ed2;} int Find(int x) {return s[x]?s[x]=Find(s[x]):x;} void dfs(int x, int d) { L[x] = ++*L, dep[x] = d; for (int i=g[x]; i;i=nxt[i]) dfs(vv[i],d+1); ID[++*ID] = x; } int main() { n=rd(),X=rd(); for (int i=2; i<=n; ++i) add(fa[i]=rd(),i); for (int i=1; i<=n; ++i) w[i] = rd(); dfs(1,1); int l = 1, r = 1; q[1] = 1; int cnt = 0; while (l<=r) { int x = q[l++]; dfn[++cnt] = x; for (int i=g[x];i;i=nxt[i]) q[++r]=vv[i]; } for (int i=1; i<=n; ++i) { int j = i; while (j<n&&dep[dfn[j+1]]==dep[dfn[i]]) ++j; ++Hash::clk; for (int k=i; k<=j; ++k) { auto u = Hash::get(w[dfn[k]]^X); if (u) add4(u,dfn[k],i),add4(dfn[k],u,i); Hash::get(w[dfn[k]]) = dfn[k]; } i = j; } for (int i=1; i<=n; ++i) { int x = ID[i]; vis[x] = 1; for (int j=f[x]; j; j=nxt2[j]) if (vis[to[j]]) { int u = Find(to[j]); add5(L[u],u,id[j]); } s[x] = fa[x]; } for (int i=1; i<=n; ++i) { s[i] = vis[i] = 0; for (int j=v[i]; j; j=nxt2[j]) { if (h[id[j]]&&vv[h[id[j]]]==to[j]) continue; add2(id[j],to[j]), ++val[to[j]]; } } for (int i=1; i<=n; ++i) { for (int j=h[i]; nxt[j]; j=nxt[j]) { int a = vv[j], b = vv[nxt[j]]; add3(a,b),add3(b,a); } } for (int i=1; i<=n; ++i) { int x = ID[i]; vis[x] = 1; for (int j=f2[x]; j; j=nxt[j]) if (vis[vv[j]]) --val[Find(vv[j])]; s[x] = fa[x]; } for (int i=1; i<=n; ++i) { int x = ID[i]; val[fa[x]] += val[x]; } int ans = 0; for (int i=1; i<=n; ++i) { int p = i^(n-val[i]); ans = (ans+p)%P; } printf("%d\n", ans); }
View Code