BZOJ 2152 聰聰可可 樹形DP

2022-05-26 23:27:10 字數 1699 閱讀 8474

給出一顆n個點帶邊權的樹(n<=20000),求隨機選擇兩個點,使得它們之間的路徑邊權是3的倍數的概率是多少。

首先總的對數是n*n,那麼只需要統計路徑邊權是3的倍數的點對數量就行了。

考慮將無根樹化為有根樹,令dp[x][i]表示以x點為路徑起點,x的某個子孫為路徑終點的邊權值模3為i的點對數量。

那麼顯然有dp[x][i]+=dp[son[x]][(i-w)%3].

考慮點對之間的路徑,要麼是它們的lca是點對中的乙個點,要麼不在點對中,因此統計一下以每個點x為lca時的路徑邊權值%3為i的點對數量。

而這兩個統計都可以在一次樹形dp中完成。因此總複雜度為o(n).

# include # include 

# include

# include

# include

# include

# include

# include

# include

# include

# include

# include

using

namespace

std;

# define lowbit(x) ((x)&(-x))

# define pi acos(-1.0

)# define eps 1e-8

# define mod

30031

# define inf

1000000000

# define mem(a,b) memset(a,b,

sizeof

(a))

# define for(i,a,n)

for(int i=a; i<=n; ++i)

# define fo(i,a,n)

for(int i=a; ii)

# define bug puts("h

");# define lch p

<<1

,l,mid

# define rch p

<<1|1,mid+1

,r# define mp make_pair

# define pb push_back

typedef pair

pii;

typedef vector

vi;# pragma comment(linker,

"/stack:1024000000,1024000000")

typedef

long

long

ll;int

scan()

while(ch>='

0'&&ch<='9')

return x*f;

}const

int n=20005;//

code begin...

struct edgeedge[n<<1

];int head[n], cnt=1, dp[n][3

], son[n];

void add_edge(int u, int v, int w)

void dfs(int x, int

fa)

for (int i=head[x]; i; i=edge[i].next)

dp[x][

0]+=1;}

intmain ()

view code

樹分治 BZOJ 2152 聰聰可可

考慮經過根的路徑,不經過根的路徑由分治得出。記子樹中的所有點到根的路徑長度對3取模以後為0的個數為a,為1的個數為b,為2的個數為c。組合數學容易算出路徑條數為a a b c 2。然後計算概率即可。include include include include include include inc...

bzoj2152 聰聰可可 點分治

點分治求路徑長度為3的倍數的鏈的條數,結果用分數表示。這道題明顯是樹形dp簡單,然而還是寫了點分治 練習所用沒辦法啊 ac 如下 include include include define n 50005 using namespace std int n,m,tot,sum,rt,ans,t 3...

BZOJ2152 聰聰可可 點分治

此題明顯可以點分治解決,對每層分治塊遞迴子塊,求出塊內有多少距離 3 0,1,2的節點,與之前其他塊的資訊歸併,即ans f 0 g 0 f 1 g 2 f 2 g 1 再把g加到f上面去即可 注意點分治求重心一定注意不要打錯字母 打混x,y之類的 注意此題題意詭異,點對要 2 n 單點也算,反過來...