正統的分塊方法應該是如「王室聯邦」一題的分塊方法。樹塊具體定義如下:
1、除根節點所在塊以外,每一塊內深度最小的結點的父親相同。這個父親被稱之為該塊的塊頂,其中特別的根節點也是塊頂。
2、每一塊內非深度最小的結點的父親一定與其處於同一塊中。
3、b<=每塊大小<=3b。b是你定義的乙個常數(b就是決定塊大小和塊個數的,修改b值會影響演算法的最終複雜度)
也就是說,對於塊頂不在塊內的結點,加上塊頂這個塊就聯通。
如何滿足這三點要求可以看**,接著會進行解釋。
這是王室聯邦那道題的**,演算法執行完後便完成了分塊。
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=1000+10;
int h[maxn],go[maxn*2],next[maxn*2],size[maxn],belong[maxn],cap[maxn],s[maxn];
int i,j,k,l,t,n,m,c,tot,top,cnt;
void add(int
x,int
y)void dfs(int
x,int
y) }
t=next[t];
}size[x]++;
}void dg(int
x,int
y,int z)
}int main()
fo(i,1,n-1)
dfs(1,0);
if (!cnt) cap[cnt=1]=1;
dg(1,0,cnt);
printf("%d\n",cnt);
fo(i,1,n) printf("%d
%c",belong[i],(i==n)?'\n':' ');
fo(i,1,cnt) printf("%d
%c",cap[i],(i==cnt)?'\n':' ');
}
belong表示乙個結點的所屬塊,cap表示乙個塊的塊頂。size[x]表示以x為根的子樹內,所屬塊塊頂不在以x為根的子樹內的結點有多少個。容易知道,這些結點最終會和x處於同一塊。
易知這樣執行演算法可以滿足樹塊定義的前兩條,我們來證明為何其滿足第三條。第三條定義也是能夠保證演算法複雜度的很重要的一條。
先看第一遍深搜,我們可以得到結論si
ze[x
]<
b ,不然就會繼續分成一塊。然後因為每次一達到b就會分成一塊,所以第一遍深搜後每塊大小至少為b至多為2b。 因為s
ize[
x]<
b ,所以第二遍深搜每塊最多被塞進b個結點。
那麼就滿足條件了!
然後我們定義塊樹,塊i所代表的結點的父親設為塊i的塊頂所屬塊,那麼可以建出塊樹。
對於通常可以使用樹分塊的題目,都是進行子樹詢問。那麼因為si
ze[x
]<
b ,所以剩餘部分不超過b個,直接暴力。然後接下來在塊樹內快速訪問所有塊頂都在子樹內的塊。
這種分塊也是可以動態的,支援加點操作的話,比如隨便弄個動態樹鏈剖分就好了(大霧。
像上面提到的題目甚至gty的妹子樹那道題,都有比樹分塊優秀的做法。
考慮上面那道題。
如果只有詢問操作,那我會做!可持久化線段樹合併就行了,可以處理出答案陣列。
有修改操作了?
對修改操作分塊,每做完一塊內所有詢問,就暴力重構可持久化線段樹。塊內的詢問,由於距離上一次重構所經過的修改操作次數很小,因此可以列舉並計算對答案的影響。
gty的妹子樹也可以使用相同思路。
目前筆者還沒有想到什麼題目就樹分塊可搞。因為樹分塊只能解決子樹問題,所以這種思路基本都能套上。
關於A 演算法的一些研究
公式 f g h 代價計算,尋路代價最小的就是我們要找的 g 表示從起點 a 移動到網格上指定方格的移動耗費 可沿斜方向移動 h 表示從指定的方格移動到終點 b 的預計耗費 h 有很多計算方法,這裡我們設定只可以上下左右移動 下面拿個例子說明一下 原文參考 假如從a尋路到b 尋路步驟 1.從起點a開...
關於mirai的一些研究
關於mirai的一些研究 配置好對應的go開發環境,即可進行編譯,生成了主要的檔案 badbot為殭屍節點的可執行檔案,cnc為主控端的可執行檔案,其它一些為輔助工具。如下圖所示編譯主控端源 go語句編寫 生成可執行檔案cnc,執行cnc,在本地開啟了23和101的埠監聽 主控端的 主要由go語言編...
關於mirai的一些研究
關於mirai的一些研究 配置好對應的go開發環境,即可進行編譯,生成了主要的檔案 badbot為殭屍節點的可執行檔案,cnc為主控端的可執行檔案,其它一些為輔助工具。如下圖所示編譯主控端源 go語句編寫 生成可執行檔案cnc,執行cnc,在本地開啟了23和101的埠監聽 主控端的 主要由go語言編...