數獨立集顯然是可以樹形dp的,問題在於本質不同。
假設已經給樹確立了乙個根並且找到了所有等效(注意是等效而不是同構)子樹,那麼對轉移稍加修改使用隔板法就行了。
關鍵在於找等效子樹。首先將樹的重心(若有兩個則加乙個點作為唯一重心)作為根。這樣任意極大等效子樹(比如某兩個等效子樹裡面的一部分等效,那麼裡面這一部分就不是極大的)一定有相同的父親,否則我們所選的根是肯定存在一棵子樹大小大於樹的一半的,與重心性質矛盾。那麼判等效就只需要考慮子樹內同構了。
同構判斷採取雜湊。這裡使用最簡單的類似字串雜湊的做法,用子樹大小雜湊。在保證同構樹雜湊值相同的前提下盡量增加變數。
#include#include#include
#include
#include
#include
using
namespace
std;
intread()
while (c>='
0'&&c<='
9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}#define n 500010
#define p 1000000007
#define ul unsigned long long
int n,p[n],p_new[n],f[n][2],size[n],q[n],inv[n],root,t=0
;ul hash[n];
struct dataedge[n
<<1],edge_new[n<<1
];void addedge(int x,int y)
void addedge_new(int x,int y)
void dfs(int k,int
from)}
void dfs2(int k,int
from)}
int findroot(int k,int
from
)int c(int n,int
m)bool cmp(const
int&a,const
int&b)
void gethash(int
k)void dp(intk)}
intmain()
dfs(
1,1);
root=findroot(1,1
); dfs(root,root);
int v=0
;
for (int i=p[root];i;i=edge[i].nxt)
if (!(n&1)&&size[edge[i].to]==(n>>1))
t=0;
if(v)
else
dfs2(root,root);
memcpy(p,p_new,
sizeof
(p));
memcpy(edge,edge_new,
sizeof
(edge));
dfs(root,root);
dp(root);
if(v)
cout
<<(f[root][0]+f[root][1])%p;
return0;
}
BZOJ3162 獨釣寒江雪
bzoj 你要給乙個樹上的每個點黑白染色,要求白點不相鄰。求本質不同的染色方案數。兩種染色方案本質相同當且僅當對樹重新標號後對應節點的顏色相同。n le 5 times10 5 首先考慮沒有本質相同那個限制怎麼做。直接設 f 表示 i 點染成黑色 白色時子樹內的方案數。轉移很簡單 f prod j ...
BZOJ 3162 獨釣寒江雪
題意是求一棵無根樹本質不同獨立集的個數 那個所謂 極寒點 的選取就是獨立集。結構相同就是樹同構,完全相同就是樹的形態和獨立集都相同。我們先求出樹的重心,就可以轉化為有根樹同構問題。令 f u 1 為在 u 的子樹中,選取 u 的方案樹,f u 0 為在 u 的子樹中,不選取 u 的方案數。得到最基本...
BZOJ3162 獨釣寒江雪(樹雜湊,動態規劃)
bzoj 忽然翻到這道題目,突然發現就是前幾天一道考試題目。題解 樹雜湊,既然只考慮這一棵樹,那麼,如果兩個點為根是同構的,他們的重心相同,所以直接找出樹的重心,以重心為根進行轉移 提前預處理每一棵子樹的雜湊值,因為相同的子樹是同構的,所以轉移相當於是可重組合的計算。對於存在兩個重心的情況,分兩個重...