首先你要先會點分治,然後動態點分治就是把點分治可持久化一下,讓其不用再每次詢問時都重新做一遍
具體就是,你考慮做點分的時候,你在每個分治重心上統計它所管轄的所有點的資訊,並計算答案。那麼每個點的資訊只會出現在它上面的分治重心中,所以我們把每層分治重心向上一層的連邊,這樣每個點的資訊只會出現都在它所有的祖先中,然後你維護需要的資訊,每次修改就往祖先跳,由於點分治只有\(log\)層,所以這顆點分樹高度只有\(log\),可以接受。
維護的資訊根據每到題變化,非常靈活。
例題:zjoi2007捉迷藏
題意:有乙個\(n\)個點的樹,初始都是黑點,現在有兩種操作,一種是將乙個點反色,另一種是詢問樹上最遠的兩個黑點的距離。
題解:求樹上最長鏈我們可以使用點分解決,對於每個分治重心,我們在它所管轄的子樹裡各找一條到分治重心的最長鏈,將最長鏈和次長鏈拼起來跟新答案。
現在我們考慮維護一下,對每個點開兩個支援刪除和取最大值的資料結構,乙個存它管轄的所有點到它上一級分治重心的距離(我們稱為a),就是一條一條的鏈。另乙個存它所有下一級的分治重心到它的最長鏈(我們稱為b),然後答案就是最長鏈和次長鏈的最大值,這個答案也要開個資料結構來維護
關於這個資料結構,\(multiset\)可以,但是好像常數巨大,我們使用兩個堆來支援就行了,具體來說就是乙個存資料,另乙個存要刪除的,取最大值的時候,如果兩個堆隊首一樣就都彈掉
關於修改,例如開燈,也就是這個點不能用,對於他自己,把b裡面的中的\(0\)給去掉,防止答案是一條以它為端點的鏈,對於它每個點分樹上的父親\(y\),從\(y\)的a中刪去這條鏈長,再處理一下這次變動對\(fa[y]\)的b的影響即可
每次改動\(b\)時,要先將答案陣列中他的貢獻刪掉,改完再加回去。
根本不能看的**(下面還有一道例題)
// luogu-judger-enable-o2
#includeusing namespace std;
typedef int sign;
typedef long long ll;
#define for(i,a,b) for(register sign i=(sign)a;i<=(sign)b;++i)
#define fordown(i,a,b) for(register sign i=(sign)a;i>=(sign)b;--i)
const int n=1e5+5;
templatebool cmax(t &a,t b)
templatet read()
templatevoid write(t x,char y)
if(x<0)
static char wr[20];
int top=0;
for(;x;x/=10)wr[++top]=x%10+'0';
while(top)putchar(wr[top--]);
putchar(y);
}void file()
int n;
vectore[n];
#define pb push_back
void input()
}typedef priority_queueq;
struct pq
void erase(int x)
int top()
return q.top();
}void pop()
q.pop();
}int sec()
int size()
}a[n],b[n],ans;
//multiseta[n],b[n],ans;
const int inf=0x3f3f3f3f;
int fa[n],size[n],rt,min,sum;
bool ban[n];
void get_root(int u,int pre)
cmax(max,sum-size[u]);
if(cmin(min,max))rt=u;
}void get_dep(int u,int pre,int dis)
return st[x][0];
}int dis(int x,int y)
char opt[20];
int m,cl[n],cnt;
void on(int x)
//insert(b[x]);
//b[x].erase(b[x].lower_bound(0));
//cerr<1?b[fa[y]].sec():-1;
//erase(b[fa[y]]);
if(a[y].size())b[fa[y]].erase(a[y].top());
a[y].erase(dis(x,fa[y]));
// cout<<"dis"<<' '<1?b[fa[y]].sec():-1;
if(t1!=t2)
}}void off(int x)
for(int y=x;fa[y];y=fa[y])
}}void work()
else
}else
// cout題意:一顆\(n\)個點的樹,每個點有點權,要求支援修改乙個點權和查詢距離某個點距離不超過\(k\)的點的點權和
題解:每個點開兩個樹狀陣列,分別維護距它不超過多少的點權之和,和它子樹中距離它父親不超過多少的點權之和
這樣在詢問時對於每乙個分治重心\(y\),貢獻就是不超過\(k-dis(x,y)\)的點權和減去不超過\(dis(x,fa[y])\)的點權和,減去的這部分會在他的父親被算回來。
#includeusing namespace std;
typedef int sign;
typedef long long ll;
#define for(i,a,b) for(register sign i=(sign)a;i<=(sign)b;++i)
#define fordown(i,a,b) for(register sign i=(sign)a;i>=(sign)b;--i)
const int n=1e5+5;
templatebool cmax(t &a,t b)
templatet read()
templatevoid write(t x,char y)
if(x<0)
static char wr[20];
int top=0;
for(;x;x/=10)wr[++top]=x%10+'0';
while(top)putchar(wr[top--]);
putchar(y);
}void file()
int n,m;
int val[n];
vectore[n];
#define pb push_back
void input()
}bool ban[n];
int rt,min,sum,size[n];
void get_root(int u,int pre)
cmax(max,sum-size[u]);
if(cmin(min,max))rt=u;
}int fa[n][21],cnt[n],dis[n][21];
vectors1[n],s2[n];
void get_dep(int u,int pre,int g,int dis)
}int sz;
void solve(int u)
void insert(int x,int v)
}int main()
點分治 動態點分治
實在拖得太久了。先扔掉資料 分治的核心是盡量把乙個整體分成接近的兩個部分,這樣遞迴處理可以讓複雜度從n 變成nlogn。兩個問題,如何區分和如何算答案。對於第乙個問題,重心,然後就是找重心的方法,兩個dfs,對於第二個問題,對於每個重心算當前塊中每個點到重心的答案,然後由重心分開的塊要把多餘的資訊去...
點分治與動態點分治
點分治一般是用於解決樹上路徑問題。樹的重心 把重心這個點割掉後,使所形成的最大的聯通塊大小最小的點。可以證明重心子樹的大小最大不會超過 n over 2 重心可以通過 dfs 一遍求出。maxsiz x 表示割掉點x後所形成的的最大的聯通塊的大小 void dfs int x,int fa max ...
動態點分治
由於蒟蒻太遜,現在才開始學動態點分治,寫乙個 blog 吧。動態點分治是利用點分治的過程,建成一顆由子樹重心連線而成的點分樹,這棵樹的高度為 log n 級別的,因此可以通過暴力跳父親完成修改操作。建立點分樹的過程,就是按照點分治的流程,記錄上級重心並連線,即可獲得一棵點分樹。點分樹的結構與原樹不相...