【bzoj3697】採藥人的路徑
題意:有每條邊有一種屬性(陰/陽)求有多少條路徑陰=陽,且能找到乙個休息點使得起點到休息點,休息點到終點陰陽平衡.
分析:本題中邊權為0/1代表陰陽,不妨將0轉化成-1,這樣若一條路徑陰陽平衡,那麼其路徑長度即為0.
點分治。
那麼要解決的問題就是如何計算經過當前根節點有多少合法路徑。
乙個乙個子樹處理
處理當前子樹時,對於當前子樹中的乙個點,其到根的路徑長度為x,則之前的子樹中的路徑長度需要是-x,才滿足第乙個條件。如何判斷是否有休息點呢。
假設當前根為root,之前子樹中的乙個點x,當前子樹中的乙個點y,dis表示到根的路徑長。
則x-y的路徑為x–root–y
長度為dis[x]+dis[y];
現已知dis[x]+dis[y]=0;
那麼假設有乙個休息點z(z!=y,z!=root),在root–y的路徑中
故dis[x]+dis[z]=0,dis[z]=-dis[x]=dis[y];
所以當存在dis[z]=dis[y] (z!=y,z!=root)是就有乙個在root–y的路徑中的休息點z了
x–root同理
設定兩個陣列g[i][0/1],f[i][0/1];
g[i][0]表示已近處理過的子樹中的點中,其到根的路徑長度為i,且其到根的路徑上存在休息點的數量,g[i][1]則表示不存在休息點
f[i][0/1]表示的是當前子樹中的點,其餘定義與g相同
為避免重複計算,計算一顆子樹貢獻時分類討論
ps:根到根這條路徑也記錄在了g陣列內,
所以對於兩端點都不在根上,即根為休息點這種情況
貢獻是:f[0][0]*(g[0][0]-1);
因為長度可能是負數,所以將下標平移??
#include
using
namespace std;
#define ll long long
int n,link[
100010
],d[
100010
],ma;
ll f[
200010][
2],g[200010][
2],ans;
bool vis[
100010];
int sum[
100010
],s,cnt[
200020
],dis[
100010
],min,root,maxx,minn,t=0;
struct dsa
e[200010];
void
insert
(int xx,
int yy,
int zz)
void
get_root
(int now,
int fa)
max=
max(max,s-sum[now]);
if(max}void
get_dis
(int now,
int fa)
cnt[dis[now]
+n]--
;return;}
void
solve
(int now)
}for
(int i=
-ma;i<=ma;i++
) g[i+n][0
]=g[i+n][1
]=0;
for(
int i=link[now]
;i;i=e[i]
.nex)
return;}
intmain()
s=n;
min=
1e9;root=0;
get_root(1
,0);
solve
(root)
;printf
("%lld"
,ans)
;}
bzoj3697 採藥人的路徑 點分治
一道拖了很久的點分治,現在把他搞定了。來自出題人hta的題解 本題可以考慮樹的點分治。問題就變成求過根滿足條件的路徑數。路徑上的休息站一定是在起點到根的路徑上,或者根到終點的路徑上。如何判斷一條從根出發的路徑是否包含休息站?只要在dfs中記錄下這條路徑的和x,同時用個標誌陣列判斷這條路徑是否存在字首...
bzoj3697 採藥人的路徑 (點分治)
原題位址 題意 採藥人的藥田是乙個樹狀結構,每條路徑上種植一種藥,有0 1兩種藥。草藥人希望選擇一條兩種藥材數目相等的路徑,且選出的路徑中有乙個可以作為休息站的節點 不包括起點和終點 滿足起點到休息站和休息站到終點的路徑也是兩種藥數量相等。他一共可以選擇多少種不同的路徑。資料範圍 n 100,000...
BZOJ 3697 採藥人的路徑 點分治
好久不做點分治的題了,正好在聯賽之前抓緊複習一下.先把邊權為 0 的置為 1 定義幾個狀態 f dis 0 1 g dis 0 1 其中 f 代表在當前遍歷的子樹內的答案.其中 f dis 0 表示到根節點距離為 dis 沒有遇到平衡點的個數,f dis 1 表示遇到平衡點的個數.然後就把 f 和 ...