JSOI2016 獨特的樹葉

2022-05-08 02:09:08 字數 1569 閱讀 5227

有一顆大小為\(n\)的樹\(a\),現加上乙個節點並打亂編號,形成樹\(b\),詢問加上的節點最後編號是多少?

判斷樹的同構顯然需要樹雜湊。

可以先將樹\(a\)中以每個節點為根的雜湊值算出來存進乙隻\(unordered\_set\)中,

然後在樹\(b\)中隨便找乙個不是葉節點的節點為根,列舉去掉乙個葉節點,看根的\(hash\)值是否能在\(unordered\_set\)中找到。

什麼?只會\(o(n^2)\)求樹的雜湊值?

我們需要思考一種\(hash\)函式,在根變動時,只影響新根和原根兩節點的值,這樣就可以每列舉到乙個點,就算出其為根時的雜湊值。因為要先\(dp\)一遍才能換根,所以複雜度為\(o(2n)\)。

並且函式需要很容易去掉某個點的影響。(異或)

\[hash_=(hash_+base)\bigoplus(hash_+base)

\bigoplus ...+size_*p+1\]

一般樹雜湊還要考慮\(deep\),當然如果你要換根\(dp\),考慮\(deep\)的影響就沒什麼用啦。

#include#include#include#include#include#include#include#define ll unsigned long long

#define re register

#define il inline

#define fp(i,a,b) for(re int i=a;i<=b;i++)

#define fq(i,a,b) for(re int i=a;i>=b;i--)

using namespace std;

const int n=5e5+10,p=1e9+7;

std::tr1::unordered_setq;

int h[n],cnt,in[n],sz[n],n;

ll hash[n],ans=1e18;

struct edgee[n<<1];

il void add(re int u,re int v);h[u]=cnt;}

il void dfs(re int u,re int fa)

hash[u]+=sz[u]*p+1;

}il void dfs1(re int u,re int fa)

}il void dfs2(re int u,re int fa)

else

}}int main()

dfs(1,0);//計算樹a雜湊值

dfs1(1,0);//換根算樹a雜湊值

memset(h,-1,sizeof(h));memset(hash,0,sizeof(hash));

fp(i,1,n)

re int i;

for(i=1;i<=n;i++) if(in[i]>1) break;//隨便找乙個不是葉節點的節點為根

dfs(i,0);//算樹b雜湊值

dfs2(i,0);//對葉子節點,試去掉它會怎麼樣;對非葉子節點,進行換根dp

printf("%lld\n",ans);

return 0;

}

JSOI2016 獨特的樹葉

點此看題 其實就是判斷樹同構,然後自然聯想到了樹has hhash hash 簡單介紹一下樹雜湊的方法,我們先求出子樹的has hhash hash 值g i g i g i 我用的是質數 自然溢位的方式,設p i p i p i 為第i ii個質數,那麼轉移 g u 1 g v p siz v g...

JSOI2016 獨特的樹葉

仙題 1.我們發現如果能夠求出來 a 樹中任何乙個點當根的時候的 hash 值,那麼就可以求出答案了。然後你隨便寫一寫 hash 策略改成 xor 發現 xor 的逆運算就是 xor 好啊!換根 dp 直接求出我們需要的,然後放到 set 去就行了。include include include i...

JSOI2016 獨特的樹葉(樹雜湊)

這個題只要求出以每個點為根的有根 無標號 樹的hash值就好了。我以前的樹雜湊是把樹轉為括號序,這個太麻煩了。一種方法是每個點的權值定義為siz,找到乙個dfs序,使得經過的點的權值字典序組最小。這個對於這道題也不方便,因為換根是可能需要字首和字尾和搞。在網上看到一種的hash是這個 f x 1 s...