<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【學習筆記】點分治

      一、概念

      有一些題要求我們統計某些點對的數量,限制一般和點間的路徑有關,\(O(n^2)\) 的時間復雜度無法承受。我們考慮首先選定一個根,此時路徑分為兩類:

      • 經過根
      • 不經過根

      其中不經過根的可以在刪掉根后在每個子樹中進行統計,遞歸求解。于是只用處理經過根的情況。那么可以將這條路徑拆成從一個點到根和從根到另一個點,方便地解決。于是只要遞歸層數合理即可獲得一個相當優秀的時間復雜度。可以證明,只要每次選擇的都是當前樹的重心,遞歸層數就是 \(O(\log n)\) 的。

      二、例題

      1.[bzoj1468]Tree

      考慮在當前樹中處理出每個點到根的距離。將所有點的距離排序,有一個 \(l\) 指針和 \(r\) 指針分別指向序列的兩端。可以發現當 \(l\)\(r\) 對應的距離之和滿足要求,\([l+1,r-1]\) 中的點就都能滿足要求。于是就累加答案,在移動 \(l\) 指針即可。否則就向左移動 \(r\) 指針。

      可以發現這樣同一個子樹內會統計到不合法的答案,那就在每個子樹內減掉即可。

      Code
      #include<bits/stdc++.h>
      #define ll long long
      #define il inline
      #define pb push_back
      using namespace std;
      namespace asbt{
      namespace cplx{bool begin;}
      const int maxn=4e4+5,inf=0x3f3f3f3f;
      int n,m,enm=1,hd[maxn];
      struct{
      	int v,w,nxt;
      }e[maxn<<1];
      il void addedge(int u,int v,int w){
      	e[++enm].v=v;
      	e[enm].w=w;
      	e[enm].nxt=hd[u];
      	hd[u]=enm;
      }
      vector<int> dv;
      int sz[maxn],mxs[maxn],dis[maxn];
      bool ban[maxn<<1];
      il void dfs1(int u,int fa){
      	sz[u]=1,mxs[u]=0;
      	for(int i=hd[u],v;i;i=e[i].nxt){
      		v=e[i].v;
      		if(ban[i]||v==fa){
      			continue;
      		}
      		dfs1(v,u);
      		sz[u]+=sz[v];
      		mxs[u]=max(mxs[u],sz[v]);
      	}
      	mxs[u]=max(mxs[u],(int)dv.size()-sz[u]);
      }
      il void dfs2(int u,int fa){
      	for(int i=hd[u],v,w;i;i=e[i].nxt){
      		v=e[i].v,w=e[i].w;
      		if(ban[i]||v==fa){
      			continue;
      		}
      		dis[v]=dis[u]+w;
      		dfs2(v,u);
      	}
      }
      il int calc(){
      	int res=0;
      	int l=0,r=dv.size()-1;
      	sort(dv.begin(),dv.end(),[](const int &x,const int &y){return dis[x]<dis[y];});
      	while(l<r){
      		if(dis[dv[l]]+dis[dv[r]]<=m){
      			res+=r-l;
      			l++;
      		}
      		else{
      			r--;
      		}
      	}
      	return res;
      }
      il void dfs3(int u,int fa){
      	dv.pb(u);
      	for(int i=hd[u],v;i;i=e[i].nxt){
      		v=e[i].v;
      		if(ban[i]||v==fa){
      			continue;
      		}
      		dfs3(v,u);
      	}
      }
      il int solve(){
      	if(dv.size()==1){
      		return 0;
      	}
      	dfs1(dv[0],0);
      	int mmxs=inf,rt=0;
      	for(int u:dv){
      		if(mmxs>mxs[u]){
      			mmxs=mxs[u],rt=u;
      		}
      	}
      	dis[rt]=0;
      	dfs2(rt,0);
      	int res=calc();
      	for(int i=hd[rt];i;i=e[i].nxt){
      		if(ban[i]){
      			continue;
      		}
      		dv.clear();
      		dfs3(e[i].v,rt);
      		res-=calc();
      	}
      	for(int i=hd[rt];i;i=e[i].nxt){
      		if(ban[i]){
      			continue;
      		}
      		ban[i]=ban[i^1]=1;
      		dv.clear();
      		dfs3(e[i].v,0);
      		res+=solve();
      	}
      	return res;
      }
      namespace cplx{
      	bool end;
      	il double usdmem(){return (&begin-&end)/1048576.0;}
      }
      int main(){
      	ios::sync_with_stdio(0),cin.tie(0);
      	cin>>n;
      	for(int i=1,u,v,w;i<n;i++){
      		cin>>u>>v>>w;
      		addedge(u,v,w);
      		addedge(v,u,w);
      	}
      	cin>>m;
      	for(int i=1;i<=n;i++){
      		dv.pb(i);
      	}
      	cout<<solve();
      	return 0;
      }
      }
      int main(){return asbt::main();}
      

      2.聰聰可可

      存儲當前到根距離對 \(3\) 取模為 \(0,1,2\) 的點數,在每個子樹上先統計答案,再將這個子樹上的點加入。

      Code
      #include<bits/stdc++.h>
      #define int long long
      #define il inline
      #define pb push_back
      #define gcd __gcd
      using namespace std;
      namespace asbt{
      namespace cplx{bool begin;}
      const int maxn=4e4+5,inf=0x3f3f3f3f;
      int n,enm=1,hd[maxn];
      struct{
      	int v,w,nxt;
      }e[maxn];
      il void addedge(int u,int v,int w){
      	e[++enm].v=v;
      	e[enm].w=w;
      	e[enm].nxt=hd[u];
      	hd[u]=enm;
      }
      vector<int> dv;
      int sz[maxn],mxs[maxn],dis[maxn];
      bool ban[maxn];
      il void dfs1(int u,int fa){
      	sz[u]=1,mxs[u]=0;
      	for(int i=hd[u],v;i;i=e[i].nxt){
      		v=e[i].v;
      		if(ban[i]||v==fa){
      			continue;
      		}
      		dfs1(v,u);
      		sz[u]+=sz[v];
      		mxs[u]=max(mxs[u],sz[v]);
      	}
      	mxs[u]=max(mxs[u],(int)dv.size()-sz[u]);
      }
      il void dfs2(int u,int fa){
      	for(int i=hd[u],v,w;i;i=e[i].nxt){
      		v=e[i].v,w=e[i].w;
      		if(ban[i]||v==fa){
      			continue;
      		}
      		dis[v]=dis[u]+w;
      		dfs2(v,u);
      	}
      }
      int ans1,ans2,nm[3];
      il void dfs3(int u,int fa){
      	ans1+=nm[((3-dis[u])%3+3)%3]<<1;
      	for(int i=hd[u],v;i;i=e[i].nxt){
      		v=e[i].v;
      		if(ban[i]||v==fa){
      			continue;
      		}
      		dfs3(v,u);
      	}
      }
      il void dfs4(int u,int fa){
      	nm[dis[u]%3]++;
      	for(int i=hd[u],v;i;i=e[i].nxt){
      		v=e[i].v;
      		if(ban[i]||v==fa){
      			continue;
      		}
      		dfs4(v,u);
      	}
      }
      il void dfs5(int u,int fa){
      	dv.pb(u);
      	for(int i=hd[u],v;i;i=e[i].nxt){
      		v=e[i].v;
      		if(ban[i]||v==fa){
      			continue;
      		}
      		dfs5(v,u);
      	}
      }
      il void solve(){
      	dfs1(dv[0],0);
      	int rt=0,mmxs=inf;
      	for(int u:dv){
      		if(mmxs>mxs[u]){
      			mmxs=mxs[u],rt=u;
      		}
      	}
      //	cout<<rt<<"\n";
      	dis[rt]=0;
      	dfs2(rt,0);
      	nm[0]=1,nm[1]=nm[2]=0;
      	ans1++;
      	for(int i=hd[rt],v;i;i=e[i].nxt){
      		if(ban[i]){
      			continue;
      		}
      		v=e[i].v;
      		dfs3(v,rt);
      		dfs4(v,rt);
      	}
      	for(int i=hd[rt];i;i=e[i].nxt){
      		if(ban[i]){
      			continue;
      		}
      		ban[i]=ban[i^1]=1;
      		dv.clear();
      		dfs5(e[i].v,0);
      		solve();
      	}
      }
      namespace cplx{
      	bool end;
      	il double usdmem(){return (&begin-&end)/1048576.0;}
      }
      signed main(){
      	ios::sync_with_stdio(0),cin.tie(0);
      	cin>>n;
      	for(int i=1,u,v,w;i<n;i++){
      		cin>>u>>v>>w;
      		addedge(u,v,w);
      		addedge(v,u,w);
      	}
      	for(int i=1;i<=n;i++){
      		dv.pb(i);
      	}
      	solve();
      	ans2=n*n;
      //	cout<<ans1<<"\n";
      	cout<<ans1/gcd(ans1,ans2)<<"/"<<ans2/gcd(ans1,ans2);
      	return 0;
      }
      }
      signed main(){return asbt::main();}
      

      3.[bzoj2599][IOI2011]Race

      存儲到根的每個距離的最小深度,類似例 \(2\) 的手法去求即可。

      Code
      #include<bits/stdc++.h>
      #define int long long
      #define il inline
      #define pb push_back
      using namespace std;
      namespace asbt{
      namespace cplx{bool begin;}
      const int maxn=4e5+5,maxm=1e6+5;
      const int inf=0x3f3f3f3f3f3f3f3f;
      int n,m,enm=1,hd[maxn];
      struct{
      	int v,w,nxt;
      }e[maxn];
      il void addedge(int u,int v,int w){
      	e[++enm].v=v;
      	e[enm].w=w;
      	e[enm].nxt=hd[u];
      	hd[u]=enm;
      }
      vector<int> dv,vp;
      int sz[maxn],mxs[maxn];
      int dep[maxn],dis[maxn];
      int mdis[maxm],ans=inf;
      bool ban[maxn];
      il void dfs1(int u,int fa){
      	sz[u]=1,mxs[u]=0;
      	for(int i=hd[u],v;i;i=e[i].nxt){
      		v=e[i].v;
      		if(ban[i]||v==fa){
      			continue;
      		}
      		dfs1(v,u);
      		sz[u]+=sz[v];
      		mxs[u]=max(mxs[u],sz[v]);
      	}
      	mxs[u]=max(mxs[u],(int)dv.size()-sz[u]);
      }
      il void dfs2(int u,int fa){
      	for(int i=hd[u],v,w;i;i=e[i].nxt){
      		v=e[i].v,w=e[i].w;
      		if(ban[i]||v==fa){
      			continue;
      		}
      		dep[v]=dep[u]+1;
      		dis[v]=dis[u]+w;
      		dfs2(v,u);
      	}
      }
      template<typename T>il void dfs3(int u,int fa,T x){
      	x(u);
      	for(int i=hd[u],v;i;i=e[i].nxt){
      		v=e[i].v;
      		if(ban[i]||v==fa){
      			continue;
      		}
      		dfs3(v,u,x);
      	}
      }
      il void solve(){
      	dfs1(dv[0],0);
      	int rt=0,mmxs=inf;
      	for(int u:dv){
      		if(mmxs>mxs[u]){
      			mmxs=mxs[u],rt=u;
      		}
      	}
      	dis[rt]=dep[rt]=0;
      	dfs2(rt,0);
      	vp.pb(0);
      	mdis[0]=0;
      	for(int i=hd[rt],v;i;i=e[i].nxt){
      		if(ban[i]){
      			continue;
      		}
      		v=e[i].v;
      		dfs3(v,rt,[](int x){if(dis[x]<=m) ans=min(ans,mdis[m-dis[x]]+dep[x]);});
      		dfs3(v,rt,[](int x){if(dis[x]<=m) mdis[dis[x]]=min(mdis[dis[x]],dep[x]),vp.pb(dis[x]);});
      	}
      	for(int x:vp){
      		mdis[x]=inf;
      	}
      	vp.clear();
      	for(int i=hd[rt],v;i;i=e[i].nxt){
      		if(ban[i]){
      			continue;
      		}
      		v=e[i].v;
      		ban[i]=ban[i^1]=1;
      		dv.clear();
      		dfs3(v,0,[](int x){dv.pb(x);});
      		solve(); 
      	}
      }
      namespace cplx{
      	bool end;
      	il double usdmem(){return (&begin-&end)/1048576.0;}
      }
      signed main(){
      	ios::sync_with_stdio(0),cin.tie(0);
      	cin>>n>>m;
      	for(int i=1,u,v,w;i<n;i++){
      		cin>>u>>v>>w;
      		u++,v++;
      		addedge(u,v,w);
      		addedge(v,u,w);
      	}
      	memset(mdis,0x3f,sizeof mdis);
      	for(int i=1;i<=n;i++){
      		dv.pb(i);
      	}
      	solve();
      	cout<<(ans>=inf?-1:ans);
      	return 0;
      }
      }
      signed main(){return asbt::main();}
      

      4.采藥人的路徑

      我們將所有 \(0\) 都記為 \(-1\),那么整條路徑陰陽平衡用點分治就很簡單了。考慮休息點的限制,那其實就是對于 \((u,v)\),從 \(u\)\(v\) 出發向上走的過程中前綴和存在 \(0\)。因為我們的 dfs 是從上往下的過程,前綴和序列需要區間加。但我們其實不用考慮前綴和序列的順序,只用看其中有沒有 \(0\) 就好了。于是可以用 FHQ-Treap 來維護。對于每個根鏈和,維護前綴和中有 \(0\) 的點的個數和沒有 \(0\) 的點的個數即可。注意特判以根為端點的情況。時間復雜度 \(O(n\log^2n)\)

      Code
      #include<bits/stdc++.h>
      #define ll long long
      #define il inline
      #define pb push_back
      using namespace std;
      namespace asbt{
      namespace cplx{bool begin;}
      const int maxn=2e5+5;
      const int inf=0x3f3f3f3f;
      int n,enm=1,hd[maxn];
      int sz[maxn],mxs[maxn];
      int nm1[maxn],nm2[maxn];
      int sum[maxn],tot;
      ll ans;
      bool ban[maxn],zero[maxn];
      vector<int> dv;
      struct{
      	int v,w,nxt;
      }e[maxn];
      il void addedge(int u,int v,int w){
      	e[++enm].v=v;
      	e[enm].w=w;
      	e[enm].nxt=hd[u];
      	hd[u]=enm;
      }
      il void dfs1(int u,int fa){
      	sz[u]=1,mxs[u]=0;
      	for(int i=hd[u],v;i;i=e[i].nxt){
      		v=e[i].v;
      		if(ban[i]||v==fa){
      			continue;
      		}
      		dfs1(v,u);
      		sz[u]+=sz[v];
      		mxs[u]=max(mxs[u],sz[v]);
      	}
      	mxs[u]=max(mxs[u],tot-sz[u]);
      }
      namespace fhq{
      	int rt,tot,ls[maxn],rs[maxn];
      	int zhi[maxn],jan[maxn],tag[maxn];
      	int ljt[maxn],top,sz[maxn];
      	il int New(int v){
      		int p=top?ljt[top--]:++tot;
      		ls[p]=rs[p]=0,sz[p]=1;
      		zhi[p]=v,tag[p]=0;
      		jan[p]=rand();
      		return p;
      	}
      	il void pushup(int id){
      		sz[id]=sz[ls[id]]+sz[rs[id]]+1;
      	}
      	il void pushdown(int id){
      		if(tag[id]){
      			tag[ls[id]]+=tag[id];
      			tag[rs[id]]+=tag[id];
      			zhi[ls[id]]+=tag[id];
      			zhi[rs[id]]+=tag[id];
      			tag[id]=0;
      		}
      	}
      	il int merge(int p,int q){
      		if(!p||!q){
      			return p+q;
      		}
      		if(jan[p]<jan[q]){
      			pushdown(p);
      			rs[p]=merge(rs[p],q);
      			pushup(p);
      			return p;
      		}
      		pushdown(q);
      		ls[q]=merge(p,ls[q]);
      		pushup(q);
      		return q;
      	}
      	il void split(int id,int &p,int &q,int v){
      		if(!id){
      			p=q=0;
      			return ;
      		}
      		pushdown(id);
      		if(zhi[id]<=v){
      			p=id;
      			split(rs[id],rs[p],q,v);
      		}
      		else{
      			q=id;
      			split(ls[id],p,ls[q],v);
      		}
      		pushup(id);
      	}
      	il void insert(int v){
      		tag[rt]+=v,zhi[rt]+=v;
      		int x,y;
      		split(rt,x,y,v);
      		rt=merge(merge(x,New(v)),y);
      	}
      	il void erase(int v){
      		int x,y,z;
      		split(rt,x,y,v-1);
      		split(y,y,z,v);
      		ljt[++top]=y;
      		rt=merge(merge(x,ls[y]),merge(rs[y],z));
      		tag[rt]-=v,zhi[rt]-=v;
      	}
      }
      il void dfs2(int u,int fa){
      	int x,y,z;
      	fhq::split(fhq::rt,x,y,-1);
      	fhq::split(y,y,z,0);
      	zero[u]=y;
      	if(!sum[u]&&fhq::sz[y]>1){
      		ans++;
      	}
      	fhq::rt=fhq::merge(fhq::merge(x,y),z);
      	if(zero[u]){
      		ans+=nm1[tot-sum[u]];
      	}
      	else{
      		ans+=nm2[tot-sum[u]];
      	}
      	for(int i=hd[u],v,w;i;i=e[i].nxt){
      		v=e[i].v,w=e[i].w;
      		if(ban[i]||v==fa){
      			continue;
      		}
      		sum[v]=sum[u]+w;
      		fhq::insert(w);
      		dfs2(v,u);
      		fhq::erase(w);
      	}
      }
      template<typename T>il void dfs3(int u,int fa,T x){
      	x(u);
      	for(int i=hd[u],v;i;i=e[i].nxt){
      		v=e[i].v;
      		if(ban[i]||v==fa){
      			continue;
      		}
      		dfs3(v,u,x);
      	}
      }
      il void solve(){
      	tot=dv.size();
      	dfs1(dv[0],0);
      	int tmp=inf,rt;
      	for(int u:dv){
      		if(tmp>mxs[u]){
      			tmp=mxs[u];
      			rt=u;
      		}
      	}
      	for(int i=0;i<=tot<<1;i++){
      		nm1[i]=nm2[i]=0;
      	}
      	for(int i=hd[rt],v,w;i;i=e[i].nxt){
      		if(ban[i]){
      			continue;
      		}
      		v=e[i].v,w=e[i].w;
      		sum[v]=w;
      		fhq::insert(w);
      		dfs2(v,rt);
      		fhq::erase(w);
      		dfs3(v,rt,[](int x){nm1[tot+sum[x]]++;if(zero[x]) nm2[tot+sum[x]]++;});
      	}
      	for(int i=hd[rt],v;i;i=e[i].nxt){
      		if(ban[i]){
      			continue;
      		}
      		v=e[i].v;
      		ban[i]=ban[i^1]=1;
      		dv.clear();
      		dfs3(v,0,[](int x){dv.pb(x);});
      		solve();
      	}
      }
      namespace cplx{
      	bool end;
      	il double usdmem(){return (&begin-&end)/1048576.0;}
      }
      int main(){
      	ios::sync_with_stdio(0),cin.tie(0);
      	cin>>n;
      	for(int i=1,u,v,w;i<n;i++){
      		cin>>u>>v>>w;
      		w=w?1:-1;
      		addedge(u,v,w);
      		addedge(v,u,w);
      	}
      	for(int i=1;i<=n;i++){
      		dv.pb(i);
      	}
      	solve();
      	cout<<ans;
      	return 0;
      }
      }
      int main(){return asbt::main();}
      

      5.「BJOI2017」樹的難題

      點分治后,對于和根相連的邊顏色為 \(w\) 的路徑,顯然我們需要將之前的路徑分成與根相連的邊為 \(w\) 的路徑和不為 \(w\) 的路徑分別統計答案。那么我們將根連出的點按照邊顏色排序,分別開兩棵平衡樹,每遇到新的顏色就將兩顆平衡樹合到一塊即可。時間復雜度 \(O(n\log^2n)\)

      Code
      #include<bits/stdc++.h>
      #define int long long
      #define il inline
      #define pb push_back
      #define mp make_pair
      #define pii pair<int,int>
      #define fir first
      #define sec second
      using namespace std;
      namespace asbt{
      namespace cplx{bool begin;}
      const int maxn=2e5+5,inf=1e18;
      int n,m,_l,_r,a[maxn],ans=-inf;
      int sz[maxn],mxsz[maxn];
      int col[maxn],dep[maxn],dis[maxn];
      bool ban[maxn];
      vector<int> dv;
      vector<pii> e[maxn];
      il void dfs1(int u,int fa,int tot){
      	sz[u]=1,mxsz[u]=0;
      	for(pii i:e[u]){
      		int v=i.fir;
      		if(v==fa||ban[v]){
      			continue;
      		}
      		dfs1(v,u,tot);
      		sz[u]+=sz[v];
      		mxsz[u]=max(mxsz[u],sz[v]);
      	}
      	mxsz[u]=max(mxsz[u],tot-sz[u]);
      }
      il void dfs2(int u,int fa){
      	for(pii i:e[u]){
      		int v=i.fir,w=i.sec;
      		if(v==fa||ban[v]){
      			continue;
      		}
      		dep[v]=dep[u]+1;
      		col[v]=w,dis[v]=dis[u];
      		if(w!=col[u]){
      			dis[v]+=a[w];
      		}
      		dfs2(v,u);
      	}
      }
      int RT[2],tot;
      int ls[maxn],rs[maxn];
      int shn[maxn],jan[maxn];
      int zhi[maxn],mxz[maxn];
      il int New(int x,int y){
      	tot++;
      	ls[tot]=rs[tot]=0;
      	shn[tot]=x,jan[tot]=rand();
      	zhi[tot]=mxz[tot]=y;
      	return tot;
      }
      il void pushup(int id){
      	mxz[id]=max({mxz[ls[id]],mxz[rs[id]],zhi[id]});
      }
      il int merge(int p,int q){
      	if(!p||!q){
      		return p+q;
      	}
      	if(jan[p]<jan[q]){
      		rs[p]=merge(rs[p],q);
      		pushup(p);
      		return p;
      	}
      	ls[q]=merge(p,ls[q]);
      	pushup(q);
      	return q;
      }
      il void split(int id,int x,int &p,int &q){
      	if(!id){
      		p=q=0;
      		return ;
      	}
      	if(shn[id]<=x){
      		p=id;
      		split(rs[id],x,rs[p],q);
      	}
      	else{
      		q=id;
      		split(ls[id],x,p,ls[q]);
      	}
      	pushup(id);
      }
      il void insert(int &id,int x,int y){
      	int p,q;
      	split(id,x,p,q);
      	id=merge(merge(p,New(x,y)),q);
      }
      il int query(int &id,int l,int r){
      	int x,y,z;
      	split(id,l-1,x,y);
      	split(y,r,y,z);
      	int res=mxz[y];
      	id=merge(merge(x,y),z);
      	return res;
      }
      il void dfs3(int &id){
      	if(!id){
      		return ;
      	}
      	dfs3(ls[id]),dfs3(rs[id]);
      	mxz[id]=zhi[id];
      	int x,y;
      	split(RT[0],shn[id],x,y);
      	RT[0]=merge(merge(x,id),y);
      	id=0;
      }
      template<typename T>il void dfs4(int u,int fa,T x){
      	x(u);
      	for(pii i:e[u]){
      		int v=i.fir;
      		if(v==fa||ban[v]){
      			continue;
      		}
      		dfs4(v,u,x);
      	}
      }
      il void solve(){
      	dfs1(dv[0],0,dv.size());
      	int rt,tmp=inf;
      	for(int u:dv){
      		if(tmp>mxsz[u]){
      			tmp=mxsz[u],rt=u;
      		}
      	}
      	col[rt]=dep[rt]=dis[rt]=0;
      	dfs2(rt,0);
      	sort(e[rt].begin(),e[rt].end(),[](pii x,pii y){return x.sec<y.sec;});
      	RT[0]=RT[1]=0;
      	mxz[0]=-inf,tot=0;
      	insert(RT[0],0,0);
      	for(int i=0,v,w;i<e[rt].size();i++){
      		v=e[rt][i].fir,w=e[rt][i].sec;
      		if(i&&w!=e[rt][i-1].sec){
      			dfs3(RT[1]);
      		}
      		if(ban[v]){
      			continue;
      		}
      		dfs4(v,rt,[=](int x){ans=max({ans,dis[x]+query(RT[0],_l-dep[x],_r-dep[x]),dis[x]+query(RT[1],_l-dep[x],_r-dep[x])-a[w]});});
      		dfs4(v,rt,[](int x){insert(RT[1],dep[x],dis[x]);});
      	}
      	ban[rt]=1;
      	for(pii i:e[rt]){
      		int v=i.fir,w=i.sec;
      		if(ban[v]){
      			continue;
      		}
      		dv.clear();
      		dfs4(v,0,[](int x){dv.pb(x);});
      		solve();
      	}
      }
      namespace cplx{
      	bool end;
      	il double usdmem(){return (&begin-&end)/1048576.0;}
      }
      signed main(){
      	ios::sync_with_stdio(0),cin.tie(0);
      	cin>>n>>m>>_l>>_r;
      	for(int i=1;i<=m;i++){
      		cin>>a[i];
      	}
      	for(int i=1,u,v,w;i<n;i++){
      		cin>>u>>v>>w;
      		e[u].pb(mp(v,w));
      		e[v].pb(mp(u,w));
      	}
      	for(int i=1;i<=n;i++){
      		dv.pb(i);
      	}
      	solve();
      	cout<<ans;
      	return 0;
      }
      }
      signed main(){return asbt::main();}
      

      三、點分樹

      在點分治過程中,對當前子樹的重心與刪去后的每個子樹的重心建立父子關系,這樣可以建成一棵新樹。對這棵樹上的每一個點維護子樹內的信息,就可以動態修改地進行點分治了。

      1.[BZOJ3730]點分樹 | 震波(模板)

      在點分樹的每一個點上用兩棵平衡樹維護距離與權值的信息(一棵維護到自己的距離,另一棵維護到點分樹上父親的距離)。修改和查詢就只需考察根鏈就好了。

      現學了一下替罪羊樹,十分的快。

      Code
      #include<bits/stdc++.h>
      #define ll long long
      #define il inline
      #define ull unsigned ll
      #define pb push_back
      using namespace std;
      namespace asbt{
      namespace cplx{bool begin;}
      namespace IO{
      	const int bufsz=1<<20;
      	char ibuf[bufsz],*p1=ibuf,*p2=ibuf;
      	#define getchar() (p1==p2&&(p2=(p1=ibuf)+fread(ibuf,1,bufsz,stdin),p1==p2)?EOF:*p1++)
      	il int read(){
      		char ch=getchar();
      		while(ch<'0'||ch>'9'){
      			ch=getchar();
      		}
      		int x=0;
      		while(ch>='0'&&ch<='9'){
      			x=(x<<1)+(x<<3)+(ch^48);
      			ch=getchar();
      		}
      		return x;
      	}
      	#undef getchar
      }
      using IO::read;
      const int maxn=1e5+5,maxm=4e6+5;
      const int inf=0x3f3f3f3f;
      int n,m,a[maxn];
      vector<int> e[maxn];
      namespace LCA{
      	int cnt,dep[maxn],dfn[maxn];
      	int idx[maxn<<1],oula[maxn<<1];
      	il void dfs(int u,int fa){
      		dep[u]=dep[fa]+1;
      		dfn[u]=++cnt;
      		idx[cnt]=u;
      		oula[cnt]=cnt;
      		for(int v:e[u]){
      			if(v==fa){
      				continue;
      			}
      			dfs(v,u);
      			oula[++cnt]=dfn[u];
      		}
      	}
      	struct{
      		int st[maxn<<1][22],Log[maxn<<1];
      		il void build(){
      			for(int i=2;i<=cnt;i++){
      				Log[i]=Log[i>>1]+1;
      			}
      			for(int i=1;i<=cnt;i++){
      				st[i][0]=oula[i];
      			}
      			for(int j=1;j<=Log[cnt];j++){
      				for(int i=1;i+(1<<j)-1<=cnt;i++){
      					st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
      				}
      			}
      		}
      		il int query(int l,int r){
      			int tmp=Log[r-l+1];
      			return min(st[l][tmp],st[r-(1<<tmp)+1][tmp]);
      		}
      	}ST;
      	il void init(){
      		dfs(1,0);
      		ST.build();
      	}
      	il int lca(int u,int v){
      		if(dfn[u]>dfn[v]){
      			swap(u,v);
      		}
      		return idx[ST.query(dfn[u],dfn[v])];
      	}
      	il int dis(int u,int v){
      		return dep[u]+dep[v]-(dep[lca(u,v)]<<1);
      	}
      }
      using LCA::dis;
      int sz[maxn],mxsz[maxn];
      int RT[maxn][2],fa[maxn];
      bool ban[maxn];
      vector<int> dv;
      il void dfs1(int u,int fa,int tot){
      	sz[u]=1,mxsz[u]=0;
      	for(int v:e[u]){
      		if(ban[v]||v==fa){
      			continue;
      		}
      		dfs1(v,u,tot);
      		sz[u]+=sz[v];
      		mxsz[u]=max(mxsz[u],sz[v]);
      	}
      	mxsz[u]=max(mxsz[u],tot-sz[u]);
      }
      template<typename T>il void dfs2(int u,int fa,T x){
      	x(u);
      	for(int v:e[u]){
      		if(v==fa||ban[v]){
      			continue;
      		}
      		dfs2(v,u,x);
      	}
      }
      namespace gtree{
      	int tot,ls[maxm],rs[maxm];
      	int pos[maxm],sz[maxm];
      	int zhi[maxm],sum[maxm];
      	int mnl[maxm],mxr[maxm];
      	int hp[maxm],cnt;
      	il void pushup(int id){
      		sz[id]=sz[ls[id]]+sz[rs[id]]+1;
      		sum[id]=sum[ls[id]]+sum[rs[id]]+zhi[id];
      		mnl[id]=ls[id]?mnl[ls[id]]:pos[id];
      		mxr[id]=rs[id]?mxr[rs[id]]:pos[id];
      	}
      	il void flatten(int id){
      		if(!id){
      			return ;
      		}
      		flatten(ls[id]);
      		hp[++cnt]=id;
      		flatten(rs[id]);
      	}
      	il void build(int &id,int l,int r){
      		if(l>r){
      			id=0;
      			return ;
      		}
      		int mid=(l+r)>>1;
      		id=hp[mid];
      		build(ls[id],l,mid-1);
      		build(rs[id],mid+1,r);
      		pushup(id);
      	}
      	il void rebuild(int &id){
      		if(max(sz[ls[id]],sz[rs[id]])>=sz[id]*0.75){
      			cnt=0;
      			flatten(id);
      			build(id,1,cnt);
      		}
      	}
      	il void insert(int &id,int x,int y){
      		if(!id){
      			id=++tot;
      			sz[id]=1;
      			mnl[id]=mxr[id]=pos[id]=x;
      			zhi[id]=sum[id]=y;
      			return ;
      		}
      		if(pos[id]==x){
      			zhi[id]+=y,sum[id]+=y;
      			return ;
      		}
      		if(x<pos[id]){
      			insert(ls[id],x,y);
      		}
      		else{
      			insert(rs[id],x,y);
      		}
      		pushup(id);
      		rebuild(id);
      	}
      	il int query(int id,int l,int r){
      		if(!id){
      			return 0;
      		}
      		if(mnl[id]>=l&&mxr[id]<=r){
      			return sum[id];
      		}
      		int res=l<=pos[id]&&r>=pos[id]?zhi[id]:0;
      		if(l<pos[id]){
      			res+=query(ls[id],l,r);
      		}
      		if(r>pos[id]){
      			res+=query(rs[id],l,r);
      		}
      		return res;
      	}
      }
      il int build(){
      	dfs1(dv[0],0,dv.size());
      	int rt,tmp=inf;
      	for(int u:dv){
      		if(tmp>mxsz[u]){
      			tmp=mxsz[u],rt=u;
      		}
      	}
      	for(int u:dv){
      		gtree::insert(RT[rt][0],dis(u,rt),a[u]);
      	}
      	ban[rt]=1;
      	for(int v:e[rt]){
      		if(ban[v]){
      			continue;
      		}
      		dv.clear();
      		dfs2(v,0,[](int x){dv.pb(x);});
      		int nrt=build();
      		fa[nrt]=rt;
      		dfs2(v,0,[=](int x){gtree::insert(RT[nrt][1],dis(x,rt),a[x]);});
      	}
      	ban[rt]=0;
      	return rt;
      }
      namespace cplx{
      	bool end;
      	il double usdmem(){return (&begin-&end)/1048576.0;}
      }
      int main(){
      	n=read(),m=read();
      	for(int i=1;i<=n;i++){
      		a[i]=read();
      	}
      	for(int i=1,u,v;i<n;i++){
      		u=read(),v=read();
      		e[u].pb(v),e[v].pb(u); 
      	}
      	LCA::init();
      	for(int i=1;i<=n;i++){
      		dv.pb(i);
      	}
      	build();
      	ll ans=0;
      	while(m--){
      		int opt,u,v;
      		opt=read(),u=read(),v=read();
      		u^=ans,v^=ans;
      		if(opt){
      			int p=u;
      			while(p){
      				gtree::insert(RT[p][0],dis(u,p),v-a[u]);
      				if(fa[p]){
      					gtree::insert(RT[p][1],dis(u,fa[p]),v-a[u]);
      				}
      				p=fa[p];
      			}
      			a[u]=v;
      		}
      		else{
      			ans=0;
      			int p=u;
      			while(p){
      				ans+=gtree::query(RT[p][0],0,v-dis(u,p));
      				if(fa[p]){
      					ans-=gtree::query(RT[p][1],0,v-dis(u,fa[p]));
      				}
      				p=fa[p];
      			}
      			printf("%lld\n",ans);
      		}
      	}
      	return 0;
      }
      }
      int main(){return asbt::main();}
      

      2.「ZJOI2015」幻想鄉戰略游戲

      考慮一個暴力的貪心:首先將決策點設為 \(1\),然后每次選擇一個比自己更優的兒子走過去,如果沒有比自己更優的兒子那決策點就是自己。

      \(u\) 為根,那么 \(u\) 的兒子 \(v\)\(u\) 優當且僅當:

      \[(sum_u-sum_v)\times\operatorname{dis}(u,v)<sum_v\times\operatorname{dis}(u,v)\\ \]

      也就是 \(2\times sum_v>sum_u\)。其中 \(sum_u\) 表示 \(u\) 的子樹內的軍隊數。顯然這樣的兒子最多只有一個。而如果 \(u\) 不是根,\(u\) 能比 \(fa_u\) 優則 \(fa_u\) 一定沒有 \(u\) 優,所以也就只用考慮兒子,不用考慮父親。

      這個做法的時間復雜度顯然和樹的深度有關。因此我們建點分樹,將樹的深度控制在 \(O(\log n)\) 級別。

      此時的思路還是一樣的:從根開始,找到自己在原樹上的更優的兒子,然后在點分數上走到包含那個兒子的子樹。沒有更優的兒子說明自己就是最優決策點。

      在點分數上對每個點維護子樹軍隊數,子樹軍隊數乘距離,以及子樹軍隊數到父親的距離。這樣對于每個決策點,計算答案就是 \(O(\log n)\) 的,從而總時間復雜度為 \(O(20n\log^2n)\)

      Code
      #include<bits/stdc++.h>
      #define int long long
      #define il inline
      #define pb push_back
      #define pii pair<int,int>
      #define mp make_pair
      #define fir first
      #define sec second
      using namespace std;
      namespace asbt{
      namespace cplx{bool begin;}
      const int maxn=1e5+5,inf=1e18;
      int n,m;
      vector<pii> e[maxn],E[maxn];
      namespace LCA{
      	int dfn[maxn],cnt,dep[maxn];
      	int oula[maxn<<1],idx[maxn<<1];
      	il void dfs(int u,int fa){
      		dfn[u]=++cnt;
      		idx[cnt]=u;
      		oula[cnt]=cnt;
      		for(pii i:e[u]){
      			int v=i.fir,w=i.sec;
      			if(v==fa){
      				continue;
      			}
      			dep[v]=dep[u]+w;
      			dfs(v,u);
      			oula[++cnt]=dfn[u];
      		}
      	}
      	struct{
      		int Log[maxn<<1],st[maxn<<1][22];
      		il void build(){
      			for(int i=2;i<=cnt;i++){
      				Log[i]=Log[i>>1]+1;
      			}
      			for(int i=1;i<=cnt;i++){
      				st[i][0]=oula[i];
      			}
      			for(int j=1;j<=Log[cnt];j++){
      				for(int i=1;i+(1<<j)-1<=cnt;i++){
      					st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
      				}
      			}
      		}
      		il int query(int l,int r){
      			int p=Log[r-l+1];
      			return min(st[l][p],st[r-(1<<p)+1][p]);
      		}
      	}ST;
      	il void init(){
      		dfs(1,0);
      		ST.build();
      	}
      	il int lca(int u,int v){
      		if(dfn[u]>dfn[v]){
      			swap(u,v);
      		}
      		return idx[ST.query(dfn[u],dfn[v])];
      	}
      	il int dis(int u,int v){
      		return dep[u]+dep[v]-dep[lca(u,v)]*2;
      	}
      }
      using LCA::dis;
      int sz[maxn],mxs[maxn],fa[maxn];
      bool ban[maxn];
      vector<int> dv;
      il void dfs1(int u,int fa,int tot){
      	sz[u]=1,mxs[u]=0;
      	for(pii i:e[u]){
      		int v=i.fir;
      		if(v==fa||ban[v]){
      			continue;
      		}
      		dfs1(v,u,tot);
      		sz[u]+=sz[v];
      		mxs[u]=max(mxs[u],sz[v]);
      	}
      	mxs[u]=max(mxs[u],tot-sz[u]);
      }
      il void dfs2(int u,int fa){
      	dv.pb(u);
      	for(pii i:e[u]){
      		int v=i.fir;
      		if(v==fa||ban[v]){
      			continue;
      		}
      		dfs2(v,u);
      	}
      }
      il int build(){
      	dfs1(dv[0],0,dv.size());
      	int rt,tmp=inf;
      	for(int u:dv){
      		if(tmp>mxs[u]){
      			tmp=mxs[u],rt=u;
      		}
      	}
      	ban[rt]=1;
      	for(pii i:e[rt]){
      		int v=i.fir;
      		if(ban[v]){
      			continue;
      		}
      		dv.clear();
      		dfs2(v,0);
      		int tmp=build();
      		E[rt].pb(mp(v,tmp));
      		fa[tmp]=rt;
      	}
      	return rt;
      }
      int sum[maxn],sumd[maxn],sumf[maxn];
      il void upd(int u,int x){
      	sum[u]+=x;
      	for(int i=u;fa[i];i=fa[i]){
      		sum[fa[i]]+=x;
      		int tmp=dis(u,fa[i]);
      		sumd[fa[i]]+=x*tmp;
      		sumf[i]+=x*tmp;
      	}
      }
      il int calc(int u){
      	int res=sumd[u];
      	for(int i=u;fa[i];i=fa[i]){
      		res+=sumd[fa[i]]-sumf[i];
      		res+=(sum[fa[i]]-sum[i])*dis(u,fa[i]);
      	}
      	return res;
      }
      il int query(int u){
      	int res=calc(u);
      	for(pii i:E[u]){
      		if(calc(i.fir)<res){
      			return query(i.sec);
      		}
      	}
      	return res;
      }
      namespace cplx{
      	bool end;
      	il double usdmem(){return (&begin-&end)/1048576.0;}
      }
      signed main(){
      	ios::sync_with_stdio(0),cin.tie(0);
      	cin>>n>>m;
      	for(int i=1,u,v,w;i<n;i++){
      		cin>>u>>v>>w;
      		e[u].pb(mp(v,w));
      		e[v].pb(mp(u,w));
      	}
      	LCA::init();
      //	for(int i=1;i<=n;i++){
      //		for(int j=1;j<=n;j++){
      //			cout<<dis(i,j)<<" ";
      //		}
      //		puts("");
      //	}
      	for(int i=1;i<=n;i++){
      		dv.pb(i);
      	}
      	int rt=build();
      	while(m--){
      		int u,x;
      		cin>>u>>x;
      		upd(u,x);
      		cout<<query(rt)<<"\n";
      	}
      	return 0;
      }
      }
      signed main(){return asbt::main();}
      

      3.「HNOI2015」開店

      是道簡單題,建出點分樹后預處理分治子樹對自己和父親的貢獻,查詢暴力爬樹即可。但是用數據結構會因為常數過大喜提 60 分,考慮到是個靜態問題,將子樹信息排序后存成 vector,查詢二分即可。

      Code
      #include<bits/stdc++.h>
      #define int long long
      #define il inline
      #define pb push_back
      #define pii pair<int,int>
      #define fir first
      #define sec second
      #define mp make_pair
      #define lwrb lower_bound
      #define uprb upper_bound
      using namespace std;
      namespace asbt{
      namespace cplx{bool begin;}
      const int maxn=1.5e5+5;
      int n,m,A,a[maxn];
      vector<pii> e[maxn];
      namespace LCA{
      	int cnt,dfn[maxn],dep[maxn];
      	int oula[maxn<<1],idx[maxn<<1];
      	il void dfs(int u,int fa){
      		dfn[u]=++cnt;
      		oula[cnt]=cnt;
      		idx[cnt]=u;
      		for(pii i:e[u]){
      			int v=i.fir,w=i.sec;
      			if(v==fa){
      				continue;
      			}
      			dep[v]=dep[u]+w;
      			dfs(v,u);
      			oula[++cnt]=dfn[u];
      		}
      	}
      	struct{
      		int Log[maxn<<1],st[maxn<<1][22];
      		il void build(){
      			for(int i=2;i<=cnt;i++){
      				Log[i]=Log[i>>1]+1;
      			}
      			for(int i=1;i<=cnt;i++){
      				st[i][0]=oula[i];
      			}
      			for(int j=1;j<=Log[cnt];j++){
      				for(int i=1;i+(1<<j)-1<=cnt;i++){
      					st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
      				}
      			}
      		}
      		il int query(int l,int r){
      			int p=Log[r-l+1];
      			return min(st[l][p],st[r-(1<<p)+1][p]);
      		}
      	}ST;
      	il void init(){
      		dfs(1,0),ST.build();
      	}
      	il int lca(int u,int v){
      		if(dfn[u]>dfn[v]){
      			swap(u,v);
      		}
      		return idx[ST.query(dfn[u],dfn[v])];
      	}
      	il int dis(int u,int v){
      		return dep[u]+dep[v]-dep[lca(u,v)]*2;
      	}
      }
      using LCA::dis;
      int tot,fa[maxn],sz[maxn],mxs[maxn];
      bool ban[maxn];
      pii hp[maxn];
      vector<int> dv,nia[maxn][2],jul[maxn][2];
      il void dfs1(int u,int fa,int tot){
      	sz[u]=1,mxs[u]=0;
      	for(pii i:e[u]){
      		int v=i.fir;
      		if(v==fa||ban[v]){
      			continue;
      		}
      		dfs1(v,u,tot);
      		sz[u]+=sz[v];
      		mxs[u]=max(mxs[u],sz[v]);
      	}
      	mxs[u]=max(mxs[u],tot-sz[u]);
      }
      template<typename T>il void dfs2(int u,int fa,T x){
      	x(u);
      	for(pii i:e[u]){
      		int v=i.fir;
      		if(v==fa||ban[v]){
      			continue;
      		}
      		dfs2(v,u,x);
      	}
      }
      il void upd(int u,int d){
      //	cout<<tot<<"\n";
      	sort(hp+1,hp+tot+1);
      	nia[u][d].pb(-1),jul[u][d].pb(0);
      	for(int i=1;i<=tot;i++){
      		nia[u][d].pb(hp[i].fir),jul[u][d].pb(hp[i].sec);
      	}
      	for(int i=1;i<=tot;i++){
      		jul[u][d][i]+=jul[u][d][i-1];
      	}
      }
      il int build(){
      	dfs1(dv[0],0,dv.size());
      	int u=0,mns=1e9;
      	for(int v:dv){
      		if(mns>mxs[v]){
      			mns=mxs[v],u=v;
      		}
      	}
      //	for(int v:dv){
      //		cout<<v<<" ";
      //	}
      //	cout<<": "<<u<<"\n";
      	tot=0,dfs2(u,0,[=](int x){hp[++tot]=mp(a[x],dis(u,x));});
      	upd(u,0);
      	ban[u]=1;
      	for(pii i:e[u]){
      		int v=i.fir;
      		if(ban[v]){
      			continue;
      		}
      		dv.clear();
      		dfs2(v,0,[](int x){dv.pb(x);});
      		int p=build();
      		fa[p]=u,tot=0;
      		dfs2(p,0,[=](int x){hp[++tot]=mp(a[x],dis(u,x));});
      		upd(p,1);
      	}
      	ban[u]=0;
      	return u;
      }
      il pii query(int u,int d,int L,int R){
      //	cout<<x.size()<<"\n";
      	int l=lwrb(nia[u][d].begin(),nia[u][d].end(),L)-nia[u][d].begin()-1;
      	int r=uprb(nia[u][d].begin(),nia[u][d].end(),R)-nia[u][d].begin()-1;
      //	cout<<l<<" "<<r<<"\n";
      	return mp(jul[u][d][r]-jul[u][d][l],r-l);
      }
      namespace cplx{
      	bool end;
      	il double usdmem(){return (&begin-&end)/1048576.0;}
      }
      signed main(){
      	ios::sync_with_stdio(0),cin.tie(0);
      	cin>>n>>m>>A;
      	for(int i=1;i<=n;i++){
      		cin>>a[i];
      	}
      	for(int i=1,u,v,w;i<n;i++){
      		cin>>u>>v>>w;
      		e[u].pb(mp(v,w));
      		e[v].pb(mp(u,w));
      	}
      	LCA::init();
      	for(int i=1;i<=n;i++){
      		dv.pb(i);
      	}
      	build();
      //	for(int i=1;i<=n;i++){
      //		cout<<fa[i]<<" ";
      //	}
      //	puts("");
      //	for(int i=1;i<=n;i++){
      //		cout<<nia[i][0].size()<<" "<<jul[i][0].size()<<" ";
      //		cout<<nia[i][1].size()<<" "<<jul[i][1].size()<<"\n";
      //	}
      	int ans=0;
      	while(m--){
      		int u,l,r;
      		cin>>u>>l>>r;
      		l=(l+ans)%A,r=(r+ans)%A;
      		if(l>r){
      			swap(l,r);
      		}
      		ans=query(u,0,l,r).fir;
      //		puts("666");
      		for(int i=u;fa[i];i=fa[i]){
      			pii res=query(i,1,l,r);
      			ans-=res.fir+res.sec*dis(u,fa[i]);
      			res=query(fa[i],0,l,r);
      			ans+=res.fir+res.sec*dis(u,fa[i]);
      		}
      		cout<<ans<<"\n";
      	}
      	return 0;
      }
      }
      signed main(){return asbt::main();}
      
      posted @ 2025-02-23 14:41  zhangxy__hp  閱讀(27)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 99热久久这里只有精品| 亚洲一区二区精品极品| 人妻丰满熟妇无码区免费| 大地资源网第二页免费观看| 精品一区二区三区不卡| 午夜福利影院不卡影院| 男女性杂交内射女bbwxz| 99久久激情国产精品| 精品亚洲精品日韩精品| 亚洲熟妇自偷自拍另欧美| 亚洲av成人在线一区| 色猫咪av在线观看| 亚洲精品无码久久一线| 999精品色在线播放| 在线观看成人年视频免费| 亚洲电影在线观看| 国产成人综合亚洲欧美日韩| 久久夜色噜噜噜亚洲av| 无码人妻h动漫| 昌乐县| 日本一区二区三区后入式| 九九热在线免费视频播放| 实拍女处破www免费看| 欧美成人午夜在线观看视频| 日韩av裸体在线播放| 亚洲伊人五月丁香激情| 国产口爆吞精在线视频2020版 | 丁香色婷婷国产精品视频| 日韩精品一区二区三区蜜臀| 亚洲精品日韩久久精品| 国产免费又黄又爽又色毛| 国产精品午夜精品福利| 国产一精品一av一免费| 国产福利社区一区二区| 神农架林区| 视频一区二区三区自拍偷拍| 日本久久99成人网站| 黑巨人与欧美精品一区| 久久天天躁狠狠躁夜夜躁2012| 成在人线av无码免费| 97av麻豆蜜桃一区二区|