AT3913-XOR Tree【状压dp】
正題
題目鏈接:https://www.luogu.com.cn/problem/AT3913
題目大意
給出一棵有邊權的樹,你每次可以選擇一條鏈讓所有的邊異或上同一個值,求最少的操作次數使得所有邊的權值都為000。
2≤n≤105,0≤w<162\leq n\leq 10^5,0\leq w<162≤n≤105,0≤w<16
解題思路
一條邊的權值可以視為連接的兩個點的權值異或,那么我們就可以把邊邊為點權了。
而鏈的異或就可以變成首尾兩個點同時異或上一個值。
那么問題就變?yōu)榱私onnn個數字你每次可以讓兩個同時異或上任意一個數,求最少操作次數使得它們都變?yōu)?span id="ozvdkddzhkzd" class="katex--inline">000。
那么顯然一樣的我們直接異或掉最優(yōu),那么現在就只剩下最多161616個不同的數了。
考慮狀壓dpdpdp,注意到如果一個集合能夠操作到000那么這個集合肯定有所有數字的異或和為000,因為無論怎么操作序列的異或和都不會變。
那么理論上如果有xxx個數字,那么我們的操作次數上限是x?1x-1x?1,但是如果我們能把集合SSS分成兩個集合T,S?TT,S-TT,S?T且這兩個集合的異或和都為000,那么就變成了x?2x-2x?2,也就是最小的值在我們盡量分最多集合得到。
設fSf_SfS?表示SSS最多分成多少個集合,然后O(316)O(3^{16})O(316)轉移就好了。
時間復雜度:O(n+316)O(n+3^{16})O(n+316)
code
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e5+10,M=16; int n,ans,a[N],v[M],c[1<<M],f[1<<M]; int main() {scanf("%d",&n);for(int i=1,x,y,w;i<n;i++){scanf("%d%d%d",&x,&y,&w);a[x]^=w;a[y]^=w;}for(int i=0;i<n;i++)v[a[i]]++;int MS=(1<<16);memset(f,0xcf,sizeof(f));for(int s=1;s<MS;s++)for(int i=0;i<16;i++)if((s>>i)&1){c[s]=c[s-(1<<i)]^i;if(!c[s])f[s]=0;break;}int S=0;f[0]=0;for(int i=1;i<16;i++)ans+=(v[i]+1)/2,S|=((v[i]&1)<<i);if(!S)return printf("%d\n",ans)&0;for(int s=1;s<MS;s++){if(c[s])continue;for(int t=(s-1)&s;t>=(s^t);t=(t-1)&s)f[s]=max(f[s],f[t]+f[s^t]+1);}printf("%d\n",ans-f[S]-1);return 0; } /* 4 0 1 5 1 2 3 2 3 5 */總結
以上是生活随笔為你收集整理的AT3913-XOR Tree【状压dp】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Win10电脑弹出配置错误的提示如何使用
- 下一篇: P6478-[NOI Online #2