題解 世界樹的考驗 狀壓dp

題解 世界樹的考驗

題目描述

MpcwYn.png

具體做法與心路歷程

考試時的心情:懵逼.jpg

二分答案走一波,不行不行,邊權這麼小,這複雜度太底了。

那麼考慮DPDP?感jiojio無法滿足後效性。。

最後瞎寫了個20pts20pts的算法,喜提00分。。

具體做法

我還是太菜了

又是一個小技巧。這種邊權異或的題目,我們往往可以把點權設爲其周圍所有邊的邊權的異或和

在這道題中使所有邊權爲0與現在使所有點權爲0是等價的。

證明:考慮度數爲1的點,因爲其點權爲0,那麼其相鄰的那一條邊邊權也必然爲0,刪去這條邊與
這個點。持續這樣刪邊,使最終只剩一個點,得證。

這樣轉換成點權後每次操作即可看成修改兩個點的點權了:因爲對於路徑上的點而言,這條路徑要經過它相鄰的兩條邊,所以可以忽略。

那麼我們首先可以抵消掉點權相同的,最後還剩下一些點權互不相同的點。對剩下的點的權值我們可以用一個狀態SS來表示:SS的第ii位表示還有點權爲ii的點存在。設fSf_S表示從狀態00變成狀態SS的最小操作次數。DPDP即可。

Code\mathcal{Code}

/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年11月05日 星期二 16時30分57秒
*******************************/
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

struct IO{
    template<typename T>
    IO & operator>>(T&res)
    {
        T q=1;char ch;
        while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
        res=(ch^48);
        while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
        res*=q;
        return *this;
    }
}cin;

const int maxn=1e5+10;
const int m=(1<<16)+10;
int n,f[m],sum[20],inf,ans,S,w[maxn];

int dfs(int S)
{
    if(!S) return 0;
    if(f[S]!=inf) return f[S];
    for(int i=0;i<16;i++) 
        if((S>>i)&1)
        {
            for(int j=0;j<16;j++)
                if(i!=j && ((S>>j)&1))
                {
                    int p=i^j,x=S^(1<<i)^(1<<j)^(1<<p);
                    f[S]=min(f[S],dfs(x)+1+((S>>p)&1));
                }
        }
    return f[S];
}

int main()
{
    //freopen("trial.in","r",stdin);
    //freopen("trial.out","w",stdout);
    cin>>n;
    int u,v,z;
    for(int i=1;i<n;i++)
    {
        cin>>u>>v>>z;
        w[u]^=z,w[v]^=z;
    }
    for(int i=0;i<n;i++) sum[w[i]]++;
    for(int i=1;i<16;i++)
        ans+=(sum[i]>>1),S+=(1<<i)*(sum[i]&1);
    memset(f,127,sizeof f); inf=f[0];
    printf("%d\n",ans+dfs(S));
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章