大致題意,實現一種樹狀資料結構,包含修改權值,新增節點,刪除邊和查詢子樹內特定權值範圍節點個數的操作。
雖然說是資料結構,而且有lct這個東西可以考慮使用,但是分塊作為一種非常快速的方法著實是賽場上的首選。樹上的分塊相對比較少見,本題可以作為樹上分塊的模板題。
樹上分塊與普通分塊類似,只不過是要按照dfs序去分塊。每次順序dfs,把節點加入塊中,當塊的節點個數大於閾值,那麼開闢新的塊。如果樹中間存在節點連線兩個塊,那麼對應就要有一條邊連線這兩條塊。可以預見,當分塊完畢之後,原本乙個含n個節點的樹,會變成乙個只含有大約
當我們需要查詢時,首先在塊的級別上查詢,到臨界點的時候再在普通節點的級別上查詢。如此,當閾值設定的好的時候,我們查詢的時間複雜度就是o(
對於刪除邊的操作也是類似。我們分刪除邊是塊間邊和塊內邊兩種情況。當是塊間邊的時候,相對比較容易處理,直接把原本塊級別上的邊刪除即可,不需要過多的維護塊節點的數值。而當是塊內邊的時候,由於一條邊的刪除,原本的乙個塊會分成兩個塊,在刪除一條邊的同時,要維護兩個塊節點的數值。
針對本題來說,需要維護在一定數值範圍內的點的個數,我們只需要對於每乙個塊,動態維護乙個vector即可。每一次塊內查詢的時候二分查詢即可。本題總的複雜度是o(n
#include#define inf 0x3f3f3f3f
#define pi 3.141592653589793
#define mod 998244353
#define ll long long
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define sf(x) scanf("%d",&x)
#define sc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
const int n = 200010;
struct blocks
inline void update(int x,int y)
inline void erase(int x)
inline void insert(int x)
inline int query(int x)
} blks[n];
int blk,tot,n,q,w[n],f[n],fa[n];
int ls[n],g[n<<1],nxt[n],e=0;
int ls[n],g[n<<1],nxt[n],e=0;
inline void addedge(int x,int y)
inline void addedge(int x,int y)
void build(int x,int father)
}int blk_query(int x,int lim)
int query(int x,int lim)
return res;
}void cut(int x,int ff)
addedge(ff,y);}}
blks[f[x]=ff].insert(w[x]);
}int main()
else if (fa[x])
} else cut(x,x);
fa[x]=0; }}
return 0;
}
牛客練習賽30
眾所周知,小k是nowcoder的 苟管理,所以小k很擅長踢樹,雖然本題與踢樹無關 小k喜歡將日期排列成yyyy mm dd的形式 位數不足添零補齊 的形式,雖然這與小k只會做回文字串這道水題無關,但小k覺得日期組成的回文串也是挺可愛的。作為乙個涼心出題人,小k決定給你乙個可愛的問題 給你兩個日期,...
牛客練習賽58 F
求帶單點修改的樹上兩點間任意子路徑長異或和。路徑長等於路徑上所有異或和。簡單模擬一下,可以發現。奇數情況下,答案是偶數點異或和。偶數情況下,就是正常的異或和。偶數點異或和也很容易處理。分深度奇偶樹狀陣列即可。但是這是對於鏈的,不能直接dfs dfsdf s序,需要剖分一下。但是我不會,所以去學了一下...
(補題)牛客小白月賽30 F 石子合併(貪心)
鏈結 題目 牛牛有 n 堆石子,每堆石子有 a i 個,牛牛每次可以選擇相鄰的兩堆石子,然後拿走少的那一堆,得到的價值是兩堆石子個數之和,直到只剩下一堆石子。如果拿走了第 i 堆石子,那麼第 i 1 堆和第 i 1 堆 就會相鄰。牛牛想知道該怎麼拿,才能使得到的價值最多。輸入 第一行乙個整數 n,1...