題目鏈結
題意:
是給一棵樹,每個節點有顏色,只有白色和黑色,切成若干部分,使得每部分只有乙個白色點。求切割方案。輸出對1000000007取模後的結果。
輸入格式
第一行僅包含乙個正整數n,表示樹的結點數量。1≤n≤105
第二行包含n-1個數字,第 i 個數字表示第 i 個結點的根,我們認為 0 號結點是整棵樹的根.第 i 個數字不超過 i,即第 i 個結點的根一定是編號小於 i 的結點。
第三行包含n個數字,第 i 個數字表示第 i-1 個結點的顏色,0表示白色,1表示黑色。
輸出格式
輸出乙個整數表示對1000000007取模後的結果。
輸入樣例1:
3
0 01 0 0
輸出樣例1:
2
輸入樣例2:
10
0 0 1 2 0 5 1 2 3
1 0 0 1 0 0 1 1 0 1
輸出樣例2:
3
思路:樹形dp
狀態設計
f[i][0]代表i節點切割出去的兒子都符合條件,且 i 點所在的連通塊有白色點。
f[i][1]代表i節點切割出去的兒子都符合條件,且 i 點所在的連通塊沒有白色點
考慮第i個節點的情況1. 當 i 點是白色點的時候。f[i][1] = 0因為他不可能存在乙個連通塊內並且不包含白色。f[i][0]由其所有的兒子的方案累乘。具體來說,對於乙個兒子j,可以選擇切割這條邊,代表這個兒子所在的連通塊包含白色,+f[j][0],也可以不切這條邊,代表兒子所在的連通塊沒有白色, +f[j][1]
2. 當 i 點是黑色點的時候。f[i][1]也是同理由其兒子選擇切割與不切割轉移來,f[i][0]則是由貢獻白點的兒子轉移,由於只能有乙個兒子貢獻白點,其他兒子貢獻的是全黑點的連通塊,或者切掉,所以各個兒子貢獻的方案之間是加法關係
這裡的由於需要 i != j 的乘法積,最好分別用字首積、字尾積陣列儲存起來
**中有詳細解釋,注意每次計算結果是否超出範圍,常取模
#include
#include
using
namespace std;
typedef
long
long ll;
const
int n=
1e6+
5,mod=
1e9+7;
int n;
int len,e[n]
,h[n]
,ne[n]
;bool color[n]
;int f[n][2
];//f[i][0]表示包含i這個節點的樹裡面沒有白色的節點,f[i][1]表示有乙個白色節點
int q[n]
,l[n]
,r[n]
;//l字首陣列,r字尾陣列,q用來儲存取出來的兒子
//鄰接表儲存樹,新增u->v這條邊
void
add(
int u,
int v)
void
dfs(
int u)
}else
//計算f[u][0],其實f[u][0]的計算與"當u為白色的時候,去算f[u][1]" 的思路類似
// 即先找出u的兒子f[i][0]的方案數,然後找到f[i][1],但是斷開他們
for(
int i=
1; i<=k; i++
) f[u][0
]=(ll)f[u][0
]*(f[q[i]][
1]+f[q[i]][
0])%mod;
//下面是最難的,計算f[u][1],因為當前的u為黑色,它能擁有白色只能說明白色來自於兒子所在的樹貢獻出來,且只能有乙個
//不妨設第個j兒子貢獻白色,則其他的兒子都要與u斷開,且其他的兒子必須包含白色
//此時方案數 x = f[j][1] * [∏(f[i][1]+f[i][0]),1<=i<=k,i!=j,k為所有u的兒子]
//因此所有的方案數f[u][1]=∑x (1<=x<=k)
//所以為了方便就要預處理,先計算除去j的前j-1的乘積和後j+1的乘積,即j的字首陣列、字尾陣列;
//下面計算字首、字尾陣列
l[0]
=r[k+1]
=1;for
(int i=
1; i<=k; i++
) l[i]
=(ll)l[i-1]
*(f[q[i]][
1]+f[q[i]][
0])%mod;
for(
int i=k; i>=
1; i--
) r[i]
=(ll)r[i+1]
*(f[q[i]][
1]+f[q[i]][
0])%mod;
//計算f[u][1]
for(
int i=
1; i<=k; i++
) f[u][1
]=(f[u][1
]+(ll)l[i-1]
*r[i+1]
%mod*f[q[i]][
1])%mod;}}
intmain()
for(
int i=
0; i
)dfs(0
);cout<
[1]<
return0;
}
切割樹 無根樹轉為有根樹
原題 1325 題意 給乙個無根樹,輸出所有點,滿足刪了這個點後剩下各個部分結點數不超過n 2 解析 知道轉變成有根樹的話,問題就直接解決了 首先用setson i 存與i連線的結點,而且因為無根樹的隨便乙個結點都可以當root,我們便選擇第乙個輸入的作為root 對於每個father,把所有兒子的...
Acwing253 普通平衡樹
插入數值x。刪除數值x 若有多個相同的數,應只刪除乙個 查詢數值x的排名 若有多個相同的數,應輸出最小的排名 查詢排名為x的數值。求數值x的前驅 前驅定義為小於x的最大的數 求數值x的後繼 後繼定義為大於x的最小的數 注意 資料保證查詢的結果一定存在。輸入格式 第一行為n,表示操作的個數。接下來n行...
acwing 貪心(哈夫曼樹)
題目 合併果子 演算法思路 每次選擇最小的兩個點合併即可,利用優先佇列 小根堆 這些點會構成一棵哈夫曼樹 完全二叉樹 處於深度最深的點會被合併最多次 可以理解為優先合併 先證明以下兩點 權值最小的兩個點,深度一定最深,並且可以互為兄弟 即優先合併,並且區域性代價最小 證明 假設最小的兩點深度不是最深...