Solution -「AT 3913」XOR Tree
\(\mathcal{Description}\)
Link.
給定一棵樹,邊 \((u,v)\) 有邊權 \(w(u,v)\)。每次操作可以使一條簡單路徑上的邊權異或任意非負整數。求最少的操作次數使得所有邊邊權為 \(0\)。
\(n\le10^5\),\(w(u,v)<16\)。
\(\mathcal{Solution}\)
好妙的題 www。
定義一個點的點權 \(val_u\) 為其所有鄰接邊邊權的異或和,即 \(val_u=\bigoplus_{(u,v)\in E}w(u,v)\)。一個至關重要的發現:所有邊權為零等價於所有點權為零。
左推右是顯然的;右推左,數歸,考慮到葉子的邊權等於點權,所以去掉所有葉子仍滿足,得證。
再考慮一次操作,除路徑兩端的點,每個點有兩條鄰接邊被異或了同一個數,所以這些點的點權不變!
非常 amazing 啊,這樣一來問題就從樹上剝離了——給一堆數,每次任選兩個數異或同一個非負整數,求把這些數變成 \(0\) 的最小操作次數。
首先,若存在 \(u\not=v,val_u=val_v\),顯然應該用一次操作處理掉它們。問題進一步轉化——給一個值域在 \([0,16)\) 的集合(無重複元素),求把這些數變成 \(0\) 的最小操作次數。
鑑於 \(16=2^4\),考慮狀壓。設 \(f(S)\) 為處理集合 \(S\) 的最小操作次數。顯然對於 \(S\) 內元素異或和不為 \(0\)
\[f(S)=\min_{T\subset S}\{\operatorname{count}(S)-1,f(T)+f(S-T)\} \]
其中,前一項是暴力兩兩異或,後者即分別處理兩個子集。
設 \(w(u,v)\) 的上限 \(W=2^k,~k\in\mathbb N\),複雜度 \(\mathcal O(3^k+n)\)。
\(\mathcal{Code}\)
#include <cstdio> #include <cstring> const int MAXN = 1e5, INF = 0x3f3f3f3f; int n, val[MAXN + 5], cnt[16], f[1 << 16], xsum[1 << 16]; inline void chkmin ( int& a, const int b ) { if ( b < a ) a = b; } int main () { scanf ( "%d", &n ); for ( int i = 1, u, v, w; i < n; ++ i ) { scanf ( "%d %d %d", &u, &v, &w ); val[u] ^= w, val[v] ^= w; } int ans = 0, S = 0; for ( int i = 0; i < n; ++ i ) ++ cnt[val[i]]; for ( int i = 1; i < 16; ++ i ) { S |= ( cnt[i] & 1 ) << i >> 1; ans += cnt[i] >> 1; } for ( int i = 1; i < 1 << 15; ++ i ) { for ( int j = 0; j < 15; ++ j ) { if ( ( i >> j ) & 1 ) { ++ f[i], xsum[i] ^= j + 1; } } -- f[i]; } for ( int s = 0; s < 1 << 15; ++ s ) { if ( xsum[s] ) continue; for ( int t = s; ; t = ( t - 1 ) & s ) { if ( ! xsum[t] && ! xsum[s ^ t] ) chkmin ( f[s], f[t] + f[s ^ t] ); if ( ! t ) break; } } printf ( "%d\n", ans + f[S] ); return 0; }