POJ 1741 Tree(点分治)
題意
一棵 \(n\) 個節點的樹,求兩點間距離不超過 \(k\) 的點對對數。
思路
點分治模板題,點分治一般用來解決樹上的路徑問題,核心在于找出重心,算經過重心的合法路徑,然后以重心把樹劈成若干個小樹分別計算,每次能把樹至少砍一半,至多遞歸到 $\log n $ 層,而每層總結點數是 \(n\) ,所以復雜度為 \(n \log n\) 。
點分除了“算經過重心的合法路徑”這一步每題不一樣外,其他部分差不多,大體代碼框架如下:
int sz[N];bool mark[N]; void CFS(int u,int f,int tot,int &C,int &Mi)//Centroid Finding Search {sz[u]=1;int res=0;EOR(i,G,u){int v=G.to[i];if(v==f||mark[v])continue;CFS(v,u,tot,C,Mi);sz[u]+=sz[v];res=max(res,sz[v]);}res=max(res,tot-sz[u]);if(res<Mi)C=u,Mi=res; } void solve(int u) {... } void dac(int u,int tot) {int Mi=1e9;CFS(u,0,tot,u,Mi);mark[u]=1;solve(u);EOR(i,G,u){int v=G.to[i];if(mark[v])continue;dac(v,sz[u]>sz[v]?sz[v]:tot-sz[u]);} }這道題,對于一個要 \(\text{solve}\) 的節點 \(u\) ,\(\text{dfs}\) 出所有從 \(u\) 出發的路徑的權值,排序,統計任意權值和不超過 \(k\) 的路徑對對數,用類似尺取的方法就可以數出經過 \(u\) 的路徑條數。然而事實上,當有兩條路徑同時經過了某個與 \(u\) 相鄰的節點 \(v\) ,那么兩條路徑就有了一條公共邊 \((u,v)\) ,就不合法了,所以應把這種情況容斥掉,具體請看代碼。
其實不容斥也可以寫,\(\text{solve}\) \(u\) 節點的時候只要將子樹一棵一棵加進去,每加一棵之前查詢一下加進去的子樹與馬上要加的子樹能配出多少條路徑,用樹狀數組維護(用 \(\text{short}\) 卡內存) ,需要一個查詢的 \(\text{dfs}\) 和更新的 \(\text{dfs}\) 。這里不放這種寫法的代碼。
代碼
#include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i) #define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i) typedef long long LL; using namespace std; const int N=3e4+5; template<const int maxn,const int maxm>struct Linked_list {int head[maxn],to[maxm],cost[maxm],nxt[maxm],tot;Linked_list(){clear();}void clear(){memset(head,-1,sizeof(head));tot=0;}void add(int u,int v,int w){to[++tot]=v,cost[tot]=w,nxt[tot]=head[u],head[u]=tot;}#define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i]) };Linked_list<N,N<<1>G; int sz[N];bool mark[N]; int A[N],Ac; int n,K,ans;void CFS(int u,int f,int tot,int &C,int &Mi) {sz[u]=1;int res=0;EOR(i,G,u){int v=G.to[i];if(v==f||mark[v])continue;CFS(v,u,tot,C,Mi);sz[u]+=sz[v];res=max(res,sz[v]);}res=max(res,tot-sz[u]);if(res<Mi)C=u,Mi=res; } void clct(int u,int f,int sum) {A[Ac++]=sum;EOR(i,G,u){int v=G.to[i],w=G.cost[i];if(v==f||mark[v])continue;clct(v,u,sum+w);} } int solve(int u,int l) {Ac=0;clct(u,0,l);sort(A,A+Ac);int res=0,i=0,j=Ac-1;while(i<j){while(i<j&&A[i]+A[j]>K)j--;res+=j-i;i++;}return res; } void dac(int rt,int tot) {int u,Mi=1e9;CFS(rt,0,tot,u,Mi);mark[u]=1;ans+=solve(u,0);EOR(i,G,u){int v=G.to[i],w=G.cost[i];if(mark[v])continue;ans-=solve(v,w);dac(v,sz[u]>sz[v]?sz[v]:tot-sz[u]);} }int main() {while(scanf("%d%d",&n,&K),n||K){G.clear();memset(mark,0,sizeof(mark));FOR(i,1,n-1){int u,v,w;scanf("%d%d%d",&u,&v,&w);G.add(u,v,w);G.add(v,u,w);}ans=0;dac(1,n);printf("%d\n",ans);}return 0; }轉載于:https://www.cnblogs.com/Paulliant/p/10159305.html
總結
以上是生活随笔為你收集整理的POJ 1741 Tree(点分治)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Beta冲刺 (7/7)
- 下一篇: element-ui table表格内