[被踩計劃] 題解 [省選聯考 2020 A 卷] 樹
阿新 • • 發佈:2020-12-24
[被踩計劃] 題解 [省選聯考 2020 A 卷] 樹
為什麼叫被踩記錄呢?因為感覺自己之前真的是太菜了,打算把之前聯賽等考過的題目做一做,看看自已以前有多菜,所以取名叫被踩記錄。
題目分析
列個表先:
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111
不難發現,對於二進位制下的第 \(i\) 位,如果每次加一,每隔 \(2^i-1\) 位這一位就會改變(由 \(0\) 到 \(1\) 或由 \(1\) 到 \(0\) ),我們可以考慮建 \(\log_2 n\) 棵森林,其中第 \(i(i\in [0,\log_2 n-1])\) 棵森林中點 \(x\)
怎麼說呢,果然自己當時還是太菜了。
參考程式碼
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ch() getchar() #define pc(x) putchar(x) using namespace std; template<typename T>void read(T&x){ static char c;static int f; for(c=ch(),f=1;c<'0'||c>'9';c=ch())if(c=='-')f=-f; for(x=0;c>='0'&&c<='9';c=ch())x=x*10+(c&15);x*=f; } template<typename T>void write(T x){ static char q[65];int cnt=0; if(x<0)pc('-'),x=-x; q[++cnt]=x%10,x/=10; while(x) q[++cnt]=x%10,x/=10; while(cnt)pc(q[cnt--]+'0'); } const int maxn=550005; int val[maxn],son[maxn],bro[maxn]; void qwq(int u,int v){ bro[v]=son[u],son[u]=v; } int pa[maxn][21],f[maxn][21],lav[maxn]; long long ans; void dfs(int u,int dp=0){ for(int i=1;(1<<i)<=dp;++i) pa[u][i]=pa[pa[u][i-1]][i-1]; lav[u]=val[u]; for(int v=son[u];v;v=bro[v]){ pa[v][0]=u;dfs(v,dp+1);lav[u]^=lav[v]; } for(int i=0;i<21;++i)if(f[u][i]){ lav[u]^=1<<i;f[pa[u][i]][i]^=1; } int no=pa[u][0];f[no][0]^=1; for(int i=0;i<20;++i){ int o=(val[u]&1)^1;val[u]>>=1; if(o)no=pa[no][i];f[no][i+1]^=1; } ans+=lav[u]; } int main(){ int n;read(n); for(int i=1;i<=n;++i)read(val[i]); for(int i=2;i<=n;++i){ int p;read(p);qwq(p,i); } dfs(1);write(ans),pc('\n'); return 0; }