1. 程式人生 > >BZOJ3697 采藥人的路徑 【點分治】

BZOJ3697 采藥人的路徑 【點分治】

math ring 有一個 esp 一個數 iostream 數據 整數 不同

題目

采藥人的藥田是一個樹狀結構,每條路徑上都種植著同種藥材。
采藥人以自己對藥材獨到的見解,對每種藥材進行了分類。大致分為兩類,一種是陰性的,一種是陽性的。
采藥人每天都要進行采藥活動。他選擇的路徑是很有講究的,他認為陰陽平衡是很重要的,所以他走的一定是兩種藥材數目相等的路徑。采藥工作是很辛苦的,所以他希望他選出的路徑中有一個可以作為休息站的節點(不包括起點和終點),滿足起點到休息站和休息站到終點的路徑也是陰陽平衡的。他想知道他一共可以選擇多少種不同的路徑。

輸入格式

第1行包含一個整數N。
接下來N-1行,每行包含三個整數a_i、b_i和t_i,表示這條路上藥材的類型。

輸出格式

輸出符合采藥人要求的路徑數目。

輸入樣例

7

1 2 0

3 1 1

2 4 0

5 2 0

6 3 1

5 7 1

輸出樣例

1

提示

對於100%的數據,N ≤ 100,000。

題解

樹上路徑計數,聯想到點分治

對於分治出來的,每一個子樹的根,我們需要找出經過該根的滿足條件的路徑數
很容易想到,如果對兩種權值賦值為-1和1的話,一條路徑權值和為0一定是陰陽互補的
先不考慮休息站,如果求陰陽互補的路徑數,只需要開一個數組記錄各種權值出現的次數【當然由於有負數就加上個n】
對於根的每棵子樹統計完時,先與之前統計的計算對答案的貢獻,路經長為-x的個數乘以之前為x的個數即可

現在考慮休息站,意味著路徑中出現中途已經出現0的情況,既然如此,那麽休息站到根的權值和與該點到根的權值和一定相等
開一個數組記錄dfs路徑上各種權值出現的次數,看看該點到根是否存在這樣可以作為休息站的點

這樣所有的點就分為有休息站和沒有休息站兩種,分開組合統計,具體就自行思考吧【看代碼也行】
復雜度\(O(nlogn)\)

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts(""); using namespace std; const int maxn = 100005,maxm = 200005,INF = 1000000000; inline int read(){ int out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57) {if (c == ‘-‘) flag = -1; c = getchar();} while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - ‘0‘; c = getchar();} return out * flag; } LL ans; int n,h[maxn],ne = 2; struct EDGE{int to,nxt,w;}ed[maxm]; inline void build(int u,int v,int w){ ed[ne] = (EDGE){v,h[u],w}; h[u] = ne++; ed[ne] = (EDGE){u,h[v],w}; h[v] = ne++; } int sum,Siz[maxn],F[maxn],rt,vis[maxn]; void getRT(int u,int f){ Siz[u] = 1; F[u] = 0; Redge(u) if (!vis[to = ed[k].to] && to != f){ getRT(to,u); Siz[u] += Siz[to]; F[u] = max(F[u],Siz[to]); } F[u] = max(F[u],sum - Siz[u]); if (F[u] < F[rt]) rt = u; } LL A[maxm],AA[maxm],B[maxm],BB[maxm]; int ex[maxm],d[maxn]; void dfs(int u,int f){ int x = d[u] + n; if (ex[x]) BB[x]++; else B[x]++; ex[x]++; Redge(u) if (!vis[to = ed[k].to] && to != f){ d[to] = d[u] + ed[k].w; dfs(to,u); } ex[x]--; } void DFS(int u,int f){ Siz[u] = 1; Redge(u) if (!vis[to = ed[k].to] && to != f){ DFS(to,u); Siz[u] += Siz[to]; } } void solve(int u){ vis[u] = true; DFS(u,0); Redge(u) if (!vis[to = ed[k].to]){ for (int i = 0; i <= Siz[to]; i++) B[n + i] = B[n - i] = BB[n + i] = BB[n - i] = 0; d[to] = ed[k].w; dfs(to,u); ans += BB[n] + BB[n] * A[n] + B[n] * AA[n] + BB[n] * AA[n] + B[n] * A[n]; for (int i = 1; i <= Siz[to]; i++){ ans += BB[n - i] * A[n + i] + B[n - i] * AA[n + i] + BB[n - i] * AA[n + i]; ans += BB[n + i] * A[n - i] + B[n + i] * AA[n - i] + BB[n + i] * AA[n - i]; } A[n] += B[n]; AA[n] += BB[n]; for (int i = 1; i <= Siz[to]; i++){ A[n - i] += B[n - i]; AA[n - i] += BB[n - i]; A[n + i] += B[n + i]; AA[n + i] += BB[n + i]; } } for (int i = 0; i <= Siz[u]; i++) A[n - i] = A[n + i] = AA[n - i] = AA[n + i] = 0; Redge(u) if (!vis[to = ed[k].to]){ sum = Siz[to]; F[rt = 0] = INF; getRT(to,0); solve(rt); } } int main(){ n = read(); int a,b,w; for (int i = 1; i < n; i++){ a = read(); b = read(); w = read(); build(a,b,w ? 1 : -1); } sum = n; F[rt = 0] = INF; getRT(1,0); solve(rt); cout << ans << endl; return 0; }

BZOJ3697 采藥人的路徑 【點分治】