1. 程式人生 > >算法學習分析-點分治 HDU 6269 Master of Subgraph

算法學習分析-點分治 HDU 6269 Master of Subgraph

mem mas 計算 當前 tps return 利用 產生 聯通

首先給出定義

點分治是一種處理樹上路徑的工具

掛出一道題目來:Master of Subgraph

這道題目讓你求所有聯通子圖加和所能產生數字,問你1到m之間,那些數字可以被產生

這道題目,假如我們利用暴力的方法去求解的話

實際上是對每個節點進行一次dfs,這樣的話會發現復雜度為O(N^2)也就是再9e6左右,再加上常數M/64,復雜度根本不夠(9e9)

我們可以利用點分治去優化復雜度

點分治的原理就是樹上的路徑產生的答案,不是在經過這個節點的就是在不經過這個節點的,那我們找到樹的重心的話,就能夠算出來經過該點的所有答案,然後依次遞歸大約logN層,這樣復雜度就變成了NlogN的程度,也就是3e4*1e3=3e7加上常數,再加上時間的寬限,完全夠用

所以點分治就是樹上分治的一種,減小重復計算的東西,不斷逐步縮小子樹的計算

其中一般來說要開 一個父節點數組,一個兒子數數組,來計算重心的位置

然後就是點分治,跟遞歸差不多就是要去計算答案

然後就是利用solve函數,每次點分治計算樹根,然後依次處理子樹的重心節點然後繼續遞歸繼續分治

  1 #include <iostream>
  2 #include <cstring>
  3 #include <vector>
  4 #include <bitset>
  5 
  6 const int MAXN=3e3+5
; 7 const int MAXM=1e5+5; 8 9 int E[MAXN][MAXN]; 10 int all[MAXN]; 11 12 int f[MAXN],son[MAXN],root,tot; 13 /* 14 分別代表f[x]的兩側孩子數目的最大值,重心的孩子數目最小 15 son代表以x為根的孩子數目 16 root是被移動的根 17 tot是當前根的孩子總數 18 */ 19 bool vis[MAXN]; 20 21 std::bitset<MAXM>b[MAXN],ans;
22 23 int n,m,val[MAXN]; 24 25 void dfs(int x,int fa){ 26 /* dfs搜索樹,將根移動到樹的重心,降低dp層數 */ 27 f[x] = 0; 28 /* 標記孩子數目為0 */ 29 son[x] = 1; 30 /* 計算孩子數目 */ 31 for(int i = 1; i <= all[x]; i++) 32 { 33 int y=E[x][i]; 34 if(!vis[y] && y!=fa) 35 { 36 dfs(y,x); 37 /* 遞歸進入子數 */ 38 f[x] = std::max(f[x],son[y]); 39 /* 計算子樹中孩子數目最多的子樹孩子數 */ 40 son[x]+=son[y]; 41 /* 累加孩子數目 */ 42 } 43 } 44 f[x]=std::max(f[x],tot-f[x]); 45 /* 計算該根節點最大孩子數其余側的數目中兩邊的最大值 */ 46 if(f[x]<f[root]) root=x; 47 /* 移動根節點,尋找重心 */ 48 } 49 50 void getdp(int x, int fa){ 51 b[x]<<=val[x]; 52 /* 累加val[x] */ 53 son[x]=1; 54 55 56 for(int i=1;i<=all[x];i++) 57 { 58 int y=E[x][i]; 59 if(!vis[y] && y!=fa) 60 { 61 b[y]=b[x]; 62 getdp(y,x); 63 son[x]+=son[y]; 64 b[x]|=b[y]; 65 } 66 } 67 } 68 69 void solve(int x){ 70 vis[x] = true; 71 b[x] = 1; 72 getdp(x,0); 73 /* 以某一點為根進行點分治 */ 74 ans|=b[x]; 75 /* 累加答案 */ 76 77 for(int i=1;i<=all[x];i++) 78 { 79 /* 子樹遞歸進行點分治 */ 80 int y=E[x][i]; 81 if(!vis[y]) 82 { 83 tot = son[y]; 84 root = 0; 85 dfs(y,x); 86 /* 先尋找樹根 */ 87 solve(root); 88 /* 遞歸分治 */ 89 } 90 } 91 } 92 93 int main(){ 94 int T; 95 std::cin>>T; 96 while(T--) 97 { 98 scanf("%d%d",&n,&m); 99 for(int i=1;i<=n;i++) all[i]=0; 100 ans.reset(); 101 memset(vis,0,sizeof(vis)); 102 for(int i=1;i<n;i++) 103 { 104 int x,y; 105 scanf("%d%d",&x,&y); 106 E[x][++all[x]]=y; 107 E[y][++all[y]]=x; 108 } 109 for(int i=1;i<=n;i++) 110 { 111 scanf("%d",&val[i]); 112 } 113 114 f[0] = n+5; 115 tot=n; 116 dfs(1,root); 117 solve(root); 118 for(int i=1;i<=m;i++) 119 { 120 printf("%d",(int)ans[i]); 121 } 122 puts(""); 123 } 124 return 0; 125 }

算法學習分析-點分治 HDU 6269 Master of Subgraph