日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

BZOJ3435[Wc2014]紫荆花之恋——动态点分治(替罪羊式点分树套替罪羊树)

發布時間:2023/12/10 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 BZOJ3435[Wc2014]紫荆花之恋——动态点分治(替罪羊式点分树套替罪羊树) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目描述

強強和萌萌是一對好朋友。有一天他們在外面閑逛,突然看到前方有一棵紫荊樹。這已經是紫荊花飛舞的季節了,無數的花瓣以肉眼可見的速度從紫荊樹上長了出來。仔細看看的話,這個大樹實際上是一個帶權樹。每個時刻它會長出一個新的葉子節點。每個節點上有一個可愛的小精靈,新長出的節點上也會同時出現一個新的小精靈。小精靈是很萌但是也很脆弱的生物,每個小精靈 i 都有一個感受能力值Ri ,小精靈 i, j 成為朋友當且僅當在樹上 i 和 j 的距離 dist(i,j) ≤ Ri + R! ,其中 dist(i, j)表示在這個樹上從 i 到 j 的唯一路徑上所有邊的邊權和。強強和萌萌很好奇每次新長出一個葉子節點之后,這個樹上總共有幾對朋友。??
我們假定這個樹一開始為空,節點按照加入的順序從 1開始編號。由于強強非常好奇, 你必須在他每次出現新節點后馬上給出總共的朋友對數,不能拖延哦。?

輸入

共有 n + 2 行。?
第一行包含一個正整數,表示測試點編號。?
第二行包含一個正整數 n ,表示總共要加入的節點數。?
我們令加入節點前的總共朋友對數是 last_ans,在一開始時它的值為0。?
接下來 n 行中第 i 行有三個數 ai, bi, ri,表示節點? i? 的父節點的編號為 ai xor (last_ans mod 10^9)?? (其中xor 表示異或,mod? 表示取余,數據保證這樣操作后得到的結果介于 1到i? –? 1之間),與父節點之間的邊權為 ci,節點 i 上小精靈的感受能力值為r!。?
注意 a1 = c1 = 0,表示 1 號點是根節點,對于 i > 1,父節點的編號至少為1。

輸出

包含 n 行,每行輸出1 個整數, 表示加入第 i 個點之后,樹上有幾對朋友。

樣例輸入

0
5
0 0 6
1 2 4
0 9 4
0 5 5
0 2 4

樣例輸出

0
1
2
4
7

提示

1<=Ci<=10000
Ai<=2*10^9
Ri<=10^9
N<=100000

?

經過一天的卡常+優化,經過從非旋轉treap到旋轉treap到替罪羊樹的改進,終于過掉了這個傳說中的神仙題!

因為這道題比較繁瑣,我們分塊講解。

點分治

按照動態點分治的常規套路,我們考慮對于一棵給定的樹,單次詢問如何用點分治處理。

當以x為分治中心時,如果聯通塊中i,j兩個點滿足條件,假設i,j距離分治中心距離分別為di,dj,那么di+dj<=ri+rj,即di-ri<=rj-dj。

那么遍歷分治中心的每個子樹,記錄每個點的ri-di,對于每個點x找到有多少個點的ri-di>=dx-rx,同樣容斥去掉兩點位于同一棵子樹中的答案。

對于維護每個點的ri-di只要開一棵平衡樹即可(因為本題強制在線不能離散化,所以不能使用權值線段樹)。

平衡樹

因為本題用到的平衡樹功能單一,所以建議使用高速平衡樹(除非旋轉treap和splay之外的其他平衡樹)。

當然如果你有高超的卡常技巧也可以用splay或非旋轉treap。

對于高速平衡樹的選擇,個人建議選擇插入時間復雜度較優的SBT或替罪羊樹(原因后面會說)。

對于替罪羊樹,經過不懈嘗試,我發現重構因子為0.86時的時間復雜度比較優秀。

動態點分治&點分樹

多次詢問顯然要用點分樹來解決,通過上面對單次查詢點分治的處理方法,我們可以知道點分樹上每個點需要維護的信息:

1、每個點開一棵平衡樹維護以它為分治中心時聯通塊內所有點的ri-di(di為聯通塊內點為到它的距離)。

2、每個點開一棵平衡樹維護以它為分治中心時聯通塊內所有點的ri-di(di為聯通塊內點到它在點分樹上父節點的距離)。

那么顯然對于靜態的樹,單次修改和查詢只要從操作點往點分樹的根爬并對沿途點進行修改或統計信息即可。

替罪羊式重構

本題的重點是邊加點邊詢問,那么問題來了:如何構建外層點分樹?

顯然不能加一個點重構一次點分樹,而點分樹也不具有treap等平衡樹的可旋性,但是我們可以像替罪羊樹一樣重構!

我們知道點分樹要求每個點的子樹中所有點都是以它為分治中心時能遍歷到的點,那么在新加一個點時我們不妨直接將這個點加到它原樹的父節點下面。

與替罪羊樹相同,我們同樣需要一個重構因子,這里因為重構時間復雜度較高,所以重構因子建議設成0.9.

在往根方向修改沿途信息時我們記錄距離根最近的一個需要重構的點,然后將這個子樹重構。

因為這棵子樹在原樹上一定是一個聯通塊,所以直接對這個聯通塊進行一遍點分治建出這個聯通塊真正的點分樹即可。

因為需要將聯通塊內所有點的vis標記都清空,所以還需要每個點開一個vector存一下這個點在點分樹上的子樹中都有哪些點。

當重構外層點分樹時,因為點分樹上的父子關系改變,所以內層平衡樹的信息也要發生相應的變化。

因為外層不是平衡樹,我們無法像平衡樹一樣通過上傳來重新改變內層信息,所以只能暴力刪除平衡樹并暴力重新插入信息。

這也是為什么要用插入時間復雜度較優的平衡樹的原因。

LCA

同樣因為動態加點,無法用RMQ或者樹鏈剖分等方法求兩點在原樹的LCA,對于新加入的每個點,只能用倍增來求LCA。

時間復雜度

本題的時間復雜度主要在外層重構及內層平衡樹的重建上。

對于外層點分樹,重構的時間復雜度每次均攤O(logn)。

對于內層平衡樹的重建,因為每個平衡樹平均logn個節點,每次插入時間復雜度O(logn),所以重建一棵平衡樹時間復雜度O(logn^2)。

一次內層重建logn棵平衡樹時間復雜度是O(logn^3)。

同樣對于一次查詢或修改需要遍歷logn個節點,每次求LCA+平衡樹上查找時間復雜度O(logn),單次查詢或修改時間復雜度O(logn^2)。

注意在點分治遞歸求重心時每層要dfs一遍整個聯通塊使當前層的分治中心獲得真正的聯通塊大小,否則會影響重構時的判斷。

綜上所述,總時間復雜度O(nlogn^3)。

#include<cmath> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; inline char _read() {static char buf[100000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline int read() {int x=0,f=1;char ch=_read();while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=_read();}while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=_read();}return x*f; } int n; ll ans; int rot; int num; int tot; int cnt; int top; int type; int point; int x,y,z; int a,b,c; int r[100010]; int d[100010]; int f[100010]; int lg[100010]; int to[200010]; int mx[100010]; int dep[100010]; int vis[100010]; int root[100010]; int size[100010]; int head[100010]; int next[200010]; int st[4000010]; int froot[100010]; int g[100010][18]; vector<int>q[100010]; const int mod=1000000000; struct balance_tree {int cot;int *flag;int q[4000010];int v[4000010];int ls[4000010];int rs[4000010];int sum[4000010];inline bool bad(int rt){if(sum[rt]*86<=100*max(sum[ls[rt]],sum[rs[rt]])){return true;}return false;}inline void dfs(int rt){if(!rt){return ;}dfs(ls[rt]);q[++cot]=rt;dfs(rs[rt]);}inline void build(int &rt,int l,int r){int mid=(l+r)>>1;rt=q[mid];if(l==r){ls[rt]=rs[rt]=0;sum[rt]=1;return ;}if(l<mid){build(ls[rt],l,mid-1);}else{ls[rt]=0;}build(rs[rt],mid+1,r);sum[rt]=sum[ls[rt]]+sum[rs[rt]]+1;}inline void rebuild(int &rt){cot=0;dfs(rt);if(cot){build(rt,1,cot);}else{rt=0;}}inline void insert(int &rt,int k){if(!rt){if(top){rt=st[top--];}else{rt=++cnt;}v[rt]=k;sum[rt]=1;return ;}sum[rt]++;if(v[rt]>=k){insert(ls[rt],k);}else{insert(rs[rt],k);}if(bad(rt)){flag=&rt;}}inline void ins(int &rt,int val){flag=0;insert(rt,val);if(flag){rebuild(*flag);}}inline void del(int &rt){if(!rt){return ;}del(ls[rt]);del(rs[rt]);v[rt]=sum[rt]=0;st[++top]=rt;rt=0;}inline int query(int root,int k){int rt=root;int ans=0;while(rt){if(v[rt]>=k){rt=ls[rt];}else{ans+=sum[ls[rt]]+1;rt=rs[rt];}}return sum[root]-ans;} }tr; inline void add(int x,int y) {next[++tot]=head[x];head[x]=tot;to[tot]=y; } inline int lca(int x,int y) {if(d[x]<d[y]){swap(x,y);}int deep=d[x]-d[y];for(int i=0;i<=lg[deep];i++){if((deep&(1<<i))){x=g[x][i];}}if(x==y){return x;}for(int i=lg[d[x]];i>=0;i--){if(g[x][i]!=g[y][i]){x=g[x][i];y=g[y][i];}}return g[x][0]; } inline int dis(int x,int y) {return dep[x]+dep[y]-(dep[lca(x,y)]<<1); } inline void getroot(int x,int fa) {size[x]=1;mx[x]=0;for(int i=head[x];i;i=next[i]){if(!vis[to[i]]&&to[i]!=fa){getroot(to[i],x);size[x]+=size[to[i]];mx[x]=max(mx[x],size[to[i]]);}}mx[x]=max(mx[x],num-size[x]);if(mx[x]<mx[rot]){rot=x;} } inline void dfs(int x,int fa,int rt) {size[x]=1;q[rt].push_back(x);tr.ins(root[rt],r[x]-dis(x,rt));if(f[rt]){tr.ins(froot[rt],r[x]-dis(x,f[rt]));}for(int i=head[x];i;i=next[i]){if(!vis[to[i]]&&to[i]!=fa){dfs(to[i],x,rt);size[x]+=size[to[i]];}} } inline void partation(int x,int fa) {getroot(x,0);tr.del(root[x]);tr.del(froot[x]);q[x].clear();f[x]=fa;vis[x]=1;dfs(x,0,x);for(int i=head[x];i;i=next[i]){if(!vis[to[i]]){num=size[to[i]];rot=0;getroot(to[i],0);partation(rot,x);}} } inline void insert(int x) {point=-1;for(int i=x;i;i=f[i]){q[i].push_back(x);tr.ins(root[i],r[x]-dis(x,i));if(f[i]){tr.ins(froot[i],r[x]-dis(x,f[i]));}size[i]++;if(f[i]&&size[i]*100>(size[f[i]]+1)*90){point=f[i];}}if(point!=-1){int len=q[point].size();for(int i=0;i<len;i++){vis[q[point][i]]=0;}num=size[point];rot=0;getroot(point,0);partation(rot,f[point]);} } inline int query(int x) {int res=0;for(int i=x;i;i=f[i]){res+=tr.query(root[i],dis(x,i)-r[x]);}for(int i=x;f[i];i=f[i]){res-=tr.query(froot[i],dis(x,f[i])-r[x]);}return res; } int main() {type=read();n=read();x=read();y=read();z=read();printf("0\n");r[1]=z;tr.ins(root[1],r[1]);q[1].push_back(1);mx[0]=1<<30;size[1]=vis[1]=1;for(int i=2;i<=n;i++){lg[i]=lg[i>>1]+1;x=read();y=read();z=read();x^=(ans%mod);r[i]=z;add(i,x);add(x,i);g[i][0]=f[i]=x;d[i]=d[x]+1;dep[i]=dep[x]+y;vis[i]=1;for(int j=1;(1<<j)<=d[i];j++){g[i][j]=g[g[i][j-1]][j-1];}ans+=query(i);printf("%lld\n",ans);insert(i);} }

轉載于:https://www.cnblogs.com/Khada-Jhin/p/10078584.html

總結

以上是生活随笔為你收集整理的BZOJ3435[Wc2014]紫荆花之恋——动态点分治(替罪羊式点分树套替罪羊树)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。