主頁 >  其他 > 「學習筆記」樹鏈剖分

「學習筆記」樹鏈剖分

2022-08-05 07:41:31 其他

「學習筆記」樹鏈剖分

點擊查看目錄

目錄
  • 「學習筆記」樹鏈剖分
    • 樹鏈剖分
      • 演算法
      • 實作
      • 例題
        • 思路
        • 代碼
    • 練習題
      • 染色
        • 思路
        • 代碼
      • QTREE
        • 思路
        • 代碼
      • [HAOI2015]樹上操作
        • 思路
        • 代碼
      • [NOIP2013 提高組] 貨車運輸
        • 思路
        • 代碼
      • [NOIP2015 提高組] 運輸計劃
        • 思路
        • 代碼
      • 遙遠的國度
        • 思路
        • 代碼

樹鏈剖分

樹鏈剖分就是把一棵樹分成多個鏈,再維護每條鏈的資訊,剖分的方法有很多種,如重鏈剖分,長鏈剖分,一般情況下,樹鏈剖分指重鏈剖分,

演算法

By OI-Wiki:

定義重子節點表示其子節點中子樹最大的子結點,如果有多個子樹最大的子結點,取其一,如果沒有子節點,就無重子節點,

定義輕子節點表示剩余的所有子結點,

從這個結點到重子節點的邊為重邊

到其他輕子節點的邊為輕邊

若干條首尾銜接的重邊構成重鏈

把落單的結點也當作重鏈,那么整棵樹就被剖分成若干條重鏈,

如圖:
image

為什么我要整段搬過來?因為除了上邊那點東西本來就沒什么可寫的,

實作

首先我們要預處理出以下陣列:

  • fa[u]:節點 \(u\) 的父親,

  • dep[u]:節點 \(u\) 的深度,

  • sz[u]:以節點 \(u\) 為根的子樹大小,

  • son[u]:節點 \(u\) 的重兒子,

  • top[u]:節點 \(u\) 所在鏈的鏈頂,

  • dfn[u]:節點 \(u\)\(\operatorname{dfs}\) 序,

  • rk[u]\(\operatorname{dfs}\) 序為 \(u\) 的節點,

這些東西并不難求,下面直接給出代碼:

void Dfs1(ll u){
	son[u]=-1,sz[u]=1;
	far(v,tu[u]){
		if(v==fa[u])continue;
		dep[v]=dep[u]+1,fa[v]=u;
		Dfs1(v);
		sz[u]+=sz[v];
		if(son[u]==-1||sz[v]>sz[son[u]])son[u]=v;//取重兒子
	}
	return;
}
void Dfs2(ll u,ll t){
	top[u]=t;
	dfn[u]=++cnt;
	rk[cnt]=u;
	if(son[u]==-1)return;
	Dfs2(son[u],t);//優先遍歷重兒子,因為這樣可以保證一條鏈上的 dfs 序連續,方便資料結構處理!!!
	far(v,tu[u])if(v!=fa[u]&&v!=son[u])Dfs2(v,v);
	return;
}

接下來問題是:如何更改和查詢?

我們讓輸入的兩個節點 \(u,v\) 不斷往上跳,具體來說:

  • 如果 \(u,v\) 在同一條鏈上,直接查詢/修改兩點的 \(\operatorname{dfs}\) 序之間的值,

  • 如果 \(u,v\) 不在同一條鏈上,直接查詢/修改 深度較深的節點 與 它所在鏈的鏈頂的節點 的 \(\operatorname{dfs}\) 序之間的值,并將其跳到它所在鏈的鏈頂的父親,

例題

思路

單點修改直接在線段樹上 \(dfs\) 序的位置改,

查詢像上面說的邊跳別查就行,

代碼

點擊查看代碼
const ll N=3e4+10,inf=1ll<<40;
ll n,q,w[N];
vector<ll>tu[N];
ll fa[N],dep[N],sz[N],son[N];//dfs1
ll cnt=0,top[N],dfn[N],rk[N];//dfs2
class SegmentTree{
public:
	class TREE{
	public:
		ll mx,va;
	}tr[N<<2];
	#define ls(p) (p<<1)
	#define rs(p) (p<<1|1)
	#define mx(p) tr[p].mx
	#define va(p) tr[p].va
	#define l_s(p) ls(p),l,mid
	#define r_s(p) rs(p),mid+1,r
	inline void Pushup(ll p){
		mx(p)=max(mx(ls(p)),mx(rs(p)));
		va(p)=va(ls(p))+va(rs(p));
	}
	void Build(ll p,ll l,ll r){
		if(l==r)mx(p)=va(p)=w[rk[l]];
		else{
			bdmd;
			Build(l_s(p));
			Build(r_s(p));
			Pushup(p);
		}
		return;
	}
	void Update(ll p,ll l,ll r,ll x,ll val){
		if(l>x||r<x)return;
		if(l==r)mx(p)=va(p)=val;
		else{
			bdmd;
			Update(l_s(p),x,val);
			Update(r_s(p),x,val);
			Pushup(p);
		}
		return;
	}
	ll QueryMax(ll p,ll l,ll r,ll le,ll ri){
		if(r<le||ri<l)return -inf;
		if(le<=l&&r<=ri)return mx(p);
		bdmd;
		return max(QueryMax(l_s(p),le,ri),QueryMax(r_s(p),le,ri));
	}
	ll QuerySum(ll p,ll l,ll r,ll le,ll ri){
		if(r<le||ri<l)return 0;
		if(le<=l&&r<=ri)return va(p);
		bdmd;
		return QuerySum(l_s(p),le,ri)+QuerySum(r_s(p),le,ri);
	}
}tr;
class KillTree{
public:
	void Dfs1(ll u){
		son[u]=-1,sz[u]=1;
		far(v,tu[u]){
			if(v==fa[u])continue;
			dep[v]=dep[u]+1,fa[v]=u;
			Dfs1(v);
			sz[u]+=sz[v];
			if(son[u]==-1||sz[v]>sz[son[u]])son[u]=v;
		}
		return;
	}
	void Dfs2(ll u,ll t){
		top[u]=t;
		dfn[u]=++cnt;
		rk[cnt]=u;
		if(son[u]==-1)return;
		Dfs2(son[u],t);
		far(v,tu[u])if(v!=fa[u]&&v!=son[u])Dfs2(v,v);
		return;
	}
	inline void Update(ll x,ll val){tr.Update(1,1,n,dfn[x],val);}
	inline ll QueryMax(ll x,ll y){
		ll num=-inf,tx=top[x],ty=top[y];
		while(tx!=ty){
			if(dep[tx]>=dep[ty])num=max(num,tr.QueryMax(1,1,n,dfn[tx],dfn[x])),x=fa[tx];
			else num=max(num,tr.QueryMax(1,1,n,dfn[ty],dfn[y])),y=fa[ty];
			tx=top[x],ty=top[y];
		}
		num=max(num,tr.QueryMax(1,1,n,min(dfn[x],dfn[y]),max(dfn[x],dfn[y])));
		return num;
	}
	inline ll QuerySum(ll x,ll y){
		ll num=0,tx=top[x],ty=top[y];
		while(tx!=ty){
			if(dep[tx]>=dep[ty])num+=tr.QuerySum(1,1,n,dfn[tx],dfn[x]),x=fa[tx];
			else num+=tr.QuerySum(1,1,n,dfn[ty],dfn[y]),y=fa[ty];
			tx=top[x],ty=top[y];
		}
		num+=tr.QuerySum(1,1,n,min(dfn[x],dfn[y]),max(dfn[x],dfn[y]));
		return num;
	}
}kt;
namespace SOLVE{
	inline ll rnt(){
		ll x=0,w=1;char c=getchar();
		while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
		while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
		return x*w;
	}
	inline void In(){
		n=rnt();
		_for(i,1,n-1){
			ll u=rnt(),v=rnt();
			tu[u].push_back(v);
			tu[v].push_back(u);
		}
		_for(i,1,n)w[i]=rnt();
		kt.Dfs1(1),kt.Dfs2(1,1),tr.Build(1,1,n);
		q=rnt();
		ll c=0;
		_for(i,1,q){
			char s[10];
			scanf("%s",s);
			ll x=rnt(),y=rnt();
			if(s[1]=='H')kt.Update(x,y);
			else if(s[1]=='M')printf("%lld\n",kt.QueryMax(x,y));
			else printf("%lld\n",kt.QuerySum(x,y));
		}
		return;
	}
}

練習題

染色

思路

教訓:板子沒寫熟別著急做難題,

我們用線段樹維護顏色段數量:如果線段樹上一個節點兩個兒子首尾顏色相同,將顏色段數量加起來后還要去掉 \(1\)

同一條鏈上顏色段數量的非常好維護,然后考慮如何合并不同鏈上的顏色段數量:

  1. 法一:記錄上次跳完后的顏色,判斷與現在的是否相等,

  2. 法二:當前節點跳到原鏈頂的父親時,判斷它與原鏈頂是否相等,

代碼

點擊查看代碼
const ll N=2e5+10,inf=1ll<<40;
ll n,q,co[N],k[N];
vector<ll>tu[N];
ll fa[N],dep[N],sz[N],son[N];//dfs1
ll cnt=0,top[N],dfn[N],rk[N];//dfs2
class KillTree{
public:
	class SegmentTree{
	public:
		class TREE{public:ll hd,tl,cnt,tag;}tr[N<<2];
		#define ls(p) (p<<1)
		#define rs(p) (p<<1|1)
		#define hd(p) tr[p].hd
		#define tl(p) tr[p].tl
		#define cn(p) tr[p].cnt
		#define ta(p) tr[p].tag
		#define l_s(p) ls(p),l,mid
		#define r_s(p) rs(p),mid+1,r
		inline void Pushup(ll p){
			hd(p)=hd(ls(p)),tl(p)=tl(rs(p));
			cn(p)=cn(ls(p))+cn(rs(p));
			if(tl(ls(p))==hd(rs(p)))--cn(p);
			return;
		}
		inline void Tag(ll p,ll color){
			cn(p)=1,ta(p)=color;
			hd(p)=color,tl(p)=color;
			return;
		}
		inline void Pushdown(ll p){
			if(ta(p)==-1)return;
			Tag(ls(p),ta(p));
			Tag(rs(p),ta(p));
			ta(p)=-1;
			return;
		}
		void Build(ll p,ll l,ll r){
			if(l==r)Tag(p,co[rk[l]]);
			else{
				bdmd;
				Build(l_s(p));
				Build(r_s(p));
				Pushup(p);
			}
			ta(p)=-1;
			return;
		}
		void Update(ll p,ll l,ll r,ll le,ll ri,ll color){
			if(ri<l||r<le)return;
			Pushdown(p);
			if(le<=l&&r<=ri)Tag(p,color);
			else{
				bdmd;
				Update(l_s(p),le,ri,color);
				Update(r_s(p),le,ri,color);
				Pushup(p);
			}
			return;
		}
		ll Query(ll p,ll l,ll r,ll le,ll ri){
			if(ri<l||r<le)return 0;
			Pushdown(p);
			if(le<=l&&r<=ri)return cn(p);
			else{
				bdmd;
				ll ans1=Query(l_s(p),le,ri);
				ll ans2=Query(r_s(p),le,ri);
				if(ans1>0&&ans2>0&&tl(ls(p))==hd(rs(p)))--ans1;
				return ans1+ans2;
			}
		}
		ll QueryP(ll p,ll l,ll r,ll x){
			if(x<l||r<x)return 0;
			Pushdown(p);
			if(l==r)return hd(p);
			else{
				bdmd;
				ll an=QueryP(l_s(p),x)+QueryP(r_s(p),x);
				return an;
			}
		}
	}tr;
	void Dfs1(ll u){
		sz[u]=1,son[u]=-1;
		far(v,tu[u]){
			if(v==fa[u])continue;
			fa[v]=u,dep[v]=dep[u]+1;
			Dfs1(v);
			sz[u]+=sz[v];
			if(son[u]==-1||sz[v]>sz[son[u]])son[u]=v;
		}
		return;
	}
	void Dfs2(ll u,ll t){
		top[u]=t;
		dfn[u]=++cnt;
		rk[cnt]=u;
		if(son[u]==-1)return;
		Dfs2(son[u],t);
		far(v,tu[u])if(v!=fa[u]&&v!=son[u])Dfs2(v,v);
		return;
	}
	inline void Build(){fa[1]=1,Dfs1(1),Dfs2(1,1),tr.Build(1,1,n);}
	inline void Update(ll x,ll y,ll color){
		ll tx=top[x],ty=top[y];
		while(tx!=ty){
			if(dep[tx]>=dep[ty])swap(x,y),swap(tx,ty);
			tr.Update(1,1,n,dfn[ty],dfn[y],color),y=fa[ty];
			tx=top[x],ty=top[y];
		}
		tr.Update(1,1,n,min(dfn[x],dfn[y]),max(dfn[x],dfn[y]),color);
	}
	inline ll Query(ll x,ll y){
		ll num=0,tx=top[x],ty=top[y];
		while(tx!=ty){
			if(dep[tx]>=dep[ty])swap(x,y),swap(tx,ty);
			num+=tr.Query(1,1,n,dfn[ty],dfn[y]),y=fa[ty];
			if(y&&tr.QueryP(1,1,n,dfn[ty])==tr.QueryP(1,1,n,dfn[y]))--num;
			tx=top[x],ty=top[y];
		}
		num+=tr.Query(1,1,n,min(dfn[x],dfn[y]),max(dfn[x],dfn[y]));
		return num;
	}
}kt;
namespace SOLVE{
	inline ll rnt(){
		ll x=0,w=1;char c=getchar();
		while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
		while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
		return x*w;
	}
	inline char rch(){
		char c=getchar();
		while(c<'A'||c>'Z')c=getchar();
		return c;
	}
	inline void In(){
		n=rnt(),q=rnt();
		_for(i,1,n)co[i]=rnt();
		_for(i,1,n-1){
			ll u=rnt(),v=rnt();
			tu[u].push_back(v);
			tu[v].push_back(u);
		}
		kt.Build();
		_for(i,1,q){
			char op=rch();
			if(op=='C'){
				ll a=rnt(),b=rnt(),op=rnt();
				kt.Update(a,b,op);
			}
			else{
				ll a=rnt(),b=rnt();
				printf("%lld\n",kt.Query(a,b));
			}
		}
		return;
	}
}

QTREE

SPOJ 在機房上不去,垃圾洛谷 RemoteJudge 交不了 C++,只能用 Vjudge 湊合了,

思路

首先為了方便處理,我們化邊權為點權,即把邊權賦到深度較大的點上成為點權,

但是會有一個問題:查詢到兩點的 \(\text{LCA}\) 時,有可能多算一條邊(即 \(\text{LCA}\) 與其父親相連的那條邊),

那么我們暫時把它的點權設為 \(-\infty\),查詢完再還原回來就可以了,

代碼

點擊查看代碼
const ll N=2e4+10,inf=1ll<<40;
ll n,q,c[N],k[N];
class Edge{public:ll u,v,w;}e[N];
vector<pair<ll,ll> >tu[N];
ll fa[N],dep[N],sz[N],son[N];
ll cnt,top[N],dfn[N],rk[N];
class KillTree{
public:
	class SegmentTree{
	public:
		ll mx[N<<4];
		#define ls(p) (p<<1)
		#define rs(p) (p<<1|1)
		#define l_s(p) ls(p),l,mid
		#define r_s(p) rs(p),mid+1,r
		void Build(ll p,ll l,ll r){
			if(l==r)mx[p]=c[rk[l]];
			else{
				bdmd;
				Build(l_s(p)),Build(r_s(p));
				mx[p]=max(mx[ls(p)],mx[rs(p)]);
			}
			return;
		}
		void Update(ll p,ll l,ll r,ll x,ll val){
			if(r<x||x<l)return;
			if(l==r)mx[p]=val;
			else{
				bdmd;
				Update(l_s(p),x,val),Update(r_s(p),x,val);
				mx[p]=max(mx[ls(p)],mx[rs(p)]);
			}
			return;
		}
		ll Query(ll p,ll l,ll r,ll le,ll ri){
			if(r<le||ri<l)return -inf;
			if(le<=l&&r<=ri)return mx[p];
			else{
				bdmd;
				return max(Query(l_s(p),le,ri),Query(r_s(p),le,ri));
			}
		}
	}tr;
	void Dfs1(ll u){
		sz[u]=1,son[u]=-1;
		far(pr,tu[u]){
			ll v=pr.first;
			if(v==fa[u])continue;
			c[v]=pr.second;
			fa[v]=u,dep[v]=dep[u]+1;
			Dfs1(v);
			sz[u]+=sz[v];
			if(son[u]==-1||sz[son[u]]<sz[v])son[u]=v;
		}
		return;
	}
	void Dfs2(ll u,ll t){
		top[u]=t;
		dfn[u]=++cnt;
		rk[cnt]=u;
		if(son[u]==-1)return;
		Dfs2(son[u],t);
		far(pr,tu[u]){
			ll v=pr.first;
			if(v!=fa[u]&&v!=son[u])
				Dfs2(v,v);
		}
		return;
	}
	inline void Build(){Dfs1(1),Dfs2(1,1),tr.Build(1,1,n);}
	inline void Update(ll x,ll y,ll z){
		if(fa[y]==x)swap(x,y);
		tr.Update(1,1,n,dfn[x],z);
		return;
	}
	inline ll GetLCA(ll x,ll y){
		ll tx=top[x],ty=top[y];
		while(tx!=ty){
			if(dep[tx]>dep[ty])swap(x,y),swap(tx,ty);
			y=fa[ty],ty=top[y];
		}
		if(dep[x]>dep[y])swap(x,y);
		return x;
	}
	inline ll Query(ll x,ll y){
		ll lca=GetLCA(x,y),la=tr.Query(1,1,n,dfn[lca],dfn[lca]);
		tr.Update(1,1,n,dfn[lca],-inf);
		ll num=-inf,tx=top[x],ty=top[y];
		while(tx!=ty){
			if(dep[tx]>dep[ty])swap(x,y),swap(tx,ty);
			num=max(num,tr.Query(1,1,n,dfn[ty],dfn[y])),y=fa[ty],ty=top[y];
		}
		num=max(num,tr.Query(1,1,n,min(dfn[x],dfn[y]),max(dfn[x],dfn[y])));
		tr.Update(1,1,n,dfn[lca],la);
		return num;
	}
}kt;
namespace SOLVE{
	inline ll rnt(){
		ll x=0,w=1;char c=getchar();
		while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
		while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
		return x*w;
	}
	inline char rch(){
		char c=getchar();
		while(c<'A'||c>'Z')c=getchar();
		return c;
	}
	inline void Clear(){
		_for(i,1,n)tu[i].clear();
		memset(e,0,sizeof(e));
		memset(fa,0,sizeof(fa));
		memset(dep,0,sizeof(dep));
		memset(sz,0,sizeof(sz));
		memset(son,0,sizeof(son));
		memset(top,0,sizeof(top));
		memset(dfn,0,sizeof(dfn));
		memset(rk,0,sizeof(rk));
		memset(c,0,sizeof(c));
		memset(k,0,sizeof(k));
		memset(kt.tr.mx,0,sizeof(kt.tr.mx));
		cnt=0;
	}
	inline void In(){
		Clear();
		n=rnt();
		_for(i,1,n-1){
			ll u=rnt(),v=rnt(),w=rnt();
			e[i]=(Edge){u,v,w};
			tu[u].push_back(make_pair(v,w));
			tu[v].push_back(make_pair(u,w));
		}
		kt.Build();
		while(1){
			char op=rch();
			if(op=='C'){
				ll a=rnt(),b=rnt();
				kt.Update(e[a].u,e[a].v,b);
			}
			else if(op=='Q'){
				ll a=rnt(),b=rnt();
				printf("%lld\n",kt.Query(a,b));
			}
			else return;
		}
		return;
	}
}

[HAOI2015]樹上操作

思路

操作 \(1,3\) 都是板子,很好求,考慮如何求操作 \(2\)

可以發現,一棵子樹內的 \(\text{DFS}\) 序相同,而我們在求重兒子時恰好也求了子樹大小,

那么我們更新一個節點 \(u\) 的子樹時,直接在線段樹上更改區間 \([dfn_u,dfn_u+size_u-1]\) 即可,

代碼

點擊查看代碼
using namespace std;
const ll N=2e5+10,inf=1ll<<40;
ll n,q,c[N];
vector<ll>tu[N];
ll fa[N],dep[N],sz[N],son[N];
ll cnt,top[N],dfn[N],rk[N];
class KillTree{
public:
	class SegmentTree{
	public:
		class TREE{public:ll val,sz,tag;}tr[N<<2];
		#define va(p) tr[p].val
		#define sz(p) tr[p].sz
		#define ta(p) tr[p].tag
		#define ls(p) (p<<1)
		#define rs(p) (p<<1|1)
		#define l_s(p) ls(p),l,mid
		#define r_s(p) rs(p),mid+1,r
		inline void Tag(ll p,ll tg){va(p)+=tg*sz(p),ta(p)+=tg;}
		inline void PushUp(ll p){va(p)=va(ls(p))+va(rs(p));}
		inline void PushDown(ll p){
			if(!ta(p))return;
			Tag(ls(p),ta(p));
			Tag(rs(p),ta(p));
			ta(p)=0;
			return;
		}
		void Build(ll p,ll l,ll r){
			sz(p)=r-l+1,ta(p)=0;
			if(l==r)va(p)=c[rk[l]];
			else{
				bdmd;
				Build(l_s(p));
				Build(r_s(p));
				PushUp(p);
			}
			return;
		}
		void Update(ll p,ll l,ll r,ll le,ll ri,ll val){
			if(ri<l||r<le)return;
			PushDown(p);
			if(le<=l&&r<=ri)Tag(p,val);
			else{
				bdmd;
				Update(l_s(p),le,ri,val);
				Update(r_s(p),le,ri,val);
				PushUp(p);
			}
			return;
		}
		ll Query(ll p,ll l,ll r,ll le,ll ri){
			if(ri<l||r<le)return 0;
			PushDown(p);
			if(le<=l&&r<=ri)return va(p);
			else{
				bdmd;
				ll ans1=Query(l_s(p),le,ri);
				ll ans2=Query(r_s(p),le,ri);
				return ans1+ans2;
			}
		}
	}tr;
	void Dfs1(ll u){
		sz[u]=1,son[u]=-1;
		far(v,tu[u]){
			if(v==fa[u])continue;
			fa[v]=u,dep[v]=dep[u]+1;
			Dfs1(v);
			sz[u]+=sz[v];
			if(son[u]==-1||sz[son[u]]<sz[v])son[u]=v;
		}
		return;
	}
	void Dfs2(ll u,ll t){
		top[u]=t;
		dfn[u]=++cnt;
		rk[cnt]=u;
		if(son[u]==-1)return;
		Dfs2(son[u],t);
		far(v,tu[u])if(v!=fa[u]&&v!=son[u])Dfs2(v,v);
		return; 
	}
	inline void Build(){Dfs1(1),Dfs2(1,1),tr.Build(1,1,n);}
	inline void UpdateP(ll x,ll val){tr.Update(1,1,n,dfn[x],dfn[x],val);}
	inline void UpdateR(ll x,ll val){tr.Update(1,1,n,dfn[x],dfn[x]+sz[x]-1,val);}
	inline ll Query(ll x){
		ll num=0,tx=top[x];
		while(tx>=1){
			num+=tr.Query(1,1,n,dfn[tx],dfn[x]);
			x=fa[tx],tx=top[x];
		}
		return num;
	}
}kt;
namespace SOLVE{
	inline ll rnt(){
		ll x=0,w=1;char c=getchar();
		while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
		while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
		return x*w;
	}
	inline char rch(){
		char c=getchar();
		while(c<'A'||c>'Z')c=getchar();
		return c;
	}
	inline void In(){
		n=rnt(),q=rnt();
		_for(i,1,n)c[i]=rnt();
		_for(i,1,n-1){
			ll u=rnt(),v=rnt();
			tu[u].push_back(v);
			tu[v].push_back(u);
		}
		kt.Build();
		_for(i,1,q){
			ll op=rnt(),a=rnt(),b=0;
			if(op==1)b=rnt(),kt.UpdateP(a,b);
			else if(op==2)b=rnt(),kt.UpdateR(a,b);
			else printf("%lld\n",kt.Query(a));
		}
		return;
	}
}

[NOIP2013 提高組] 貨車運輸

思路

給出的不是一棵樹,但是我們可以發現查詢只與路徑上最小值相關,而不管距離,所以我們只留下一些有用的邊即可,即求出一棵最小生成樹,

剩下的就是板子了,

代碼

點擊查看代碼
const ll N=1e5+10,inf=1ll<<40;
ll n,m,q,c[N],k[N];
vector<pair<ll,ll> >tu[N];
ll fa[N],dep[N],sz[N],son[N];
ll cnt,top[N],dfn[N],rk[N];
class BCJ{
public:
	ll f[N];
	inline void Pre(){_for(i,1,n)f[i]=i;}
	ll Find(ll x){return (f[x]==x)?x:(f[x]=Find(f[x]));}
	inline void Merge(ll x,ll y){f[y]=x;}
}bcj;
namespace Kru{
	class Edge{
	public:
		ll u,v,w;
		inline bool operator<(const Edge &d)const{return w>d.w;}
	}e[N];
	inline void Add(ll i,ll u,ll v,ll w){e[i].u=u,e[i].v=v,e[i].w=w;}
	inline void Solve(){
		sort(e+1,e+m+1);
		ll k=0;
		bcj.Pre();
		_for(i,1,m){
			ll fu=bcj.Find(e[i].u),fv=bcj.Find(e[i].v);
			if(fu!=fv){
				tu[e[i].u].push_back(make_pair(e[i].v,e[i].w));
				tu[e[i].v].push_back(make_pair(e[i].u,e[i].w));
				bcj.Merge(fu,fv);
				++k;
			}
			if(k==n-1)break;
		}
		return;
	}
}
class KillTree{
public:
	class SegmentTree{
	public:
		class TREE{public:ll mn;}tr[N<<2];
		#define mn(p) tr[p].mn
		#define ls(p) (p<<1)
		#define rs(p) (p<<1|1)
		#define l_s(p) (p<<1),l,mid
		#define r_s(p) (p<<1|1),mid+1,r
		void Build(ll p,ll l,ll r){
			if(l==r)mn(p)=c[rk[l]]?c[rk[l]]:inf;
			else{
				bdmd;
				Build(l_s(p)),Build(r_s(p));
				mn(p)=min(mn(ls(p)),mn(rs(p)));
			}
			return;
		}
		ll Query(ll p,ll l,ll r,ll le,ll ri){
			if(ri<l||r<le)return inf;
			if(le<=l&&r<=ri)return mn(p);
			else{
				bdmd;
				return min(Query(l_s(p),le,ri),Query(r_s(p),le,ri));
			}
		}
	}tr;
	void Dfs1(ll u){
		sz[u]=1,son[u]=-1;
		far(pr,tu[u]){
			ll v=pr.first,w=pr.second;
			if(v==fa[u])continue;
			c[v]=w,fa[v]=u,dep[v]=dep[u]+1;
			Dfs1(v);
			sz[u]+=sz[v];
			if(son[u]==-1||sz[son[u]]<sz[v])son[u]=v;
		}
		return;
	}
	void Dfs2(ll u,ll t){
		top[u]=t;
		dfn[u]=++cnt;
		rk[cnt]=u;
		if(son[u]==-1)return;
		Dfs2(son[u],t);
		far(pr,tu[u]){
			ll v=pr.first;
			if(v!=fa[u]&&v!=son[u])Dfs2(v,v);
		}
		return;
	}
	inline void Build(){Dfs1(1),Dfs2(1,1),tr.Build(1,1,n);}
	inline ll Query(ll x,ll y){
		if(bcj.Find(x)!=bcj.Find(y))return -1;
		ll num=inf,tx=top[x],ty=top[y];
		while(tx!=ty){
			if(dep[tx]<dep[ty])swap(x,y),swap(tx,ty);
			num=min(num,tr.Query(1,1,n,dfn[tx],dfn[x]));
			x=fa[tx],tx=top[x];
		}
		if(x!=y)num=min(num,tr.Query(1,1,n,min(dfn[x],dfn[y])+1,max(dfn[x],dfn[y])));
		return num;
	}
}kt;
namespace SOLVE{
	inline ll rnt(){
		ll x=0,w=1;char c=getchar();
		while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
		while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
		return x*w;
	}
	inline char rch(){
		char c=getchar();
		while(c<'A'||c>'Z')c=getchar();
		return c;
	}
	inline void In(){
		n=rnt(),m=rnt();
		_for(i,1,m){
			ll u=rnt(),v=rnt(),w=rnt();
			Kru::Add(i,u,v,w);
		}
		Kru::Solve();
		kt.Build();
		q=rnt();
		_for(i,1,q){
			ll x=rnt(),y=rnt();
			printf("%lld\n",kt.Query(x,y));
		}
		return;
	}
}

[NOIP2015 提高組] 運輸計劃

思路

毒瘤好題,

首先老套路化邊權為點權,

考慮去掉哪一條邊才會產生貢獻:顯然是耗時最久的飛船經過的某條邊,

那么我們維護一個陣列 \(q\)\(q_i\) 表示不經過節點 \(i\) 的飛船中的最長時間,這樣只要列舉耗時最久的飛船經過的所有邊就可以了,

那么如何維護一個陣列 \(q\) 呢?我們可以發現兩點之間的路徑會被樹鏈截成若干個不相交的 \(\text{dfs}\) 序區間,那么我們對這些區間排個序,將相鄰區間之間形成的區間打上當前答案的標記即可,

然后說一下我踩過的坑:

  • 讀錯題,飛船應是同時出發的,(\(0\text{pts}\longrightarrow15\text{pts}\)

  • #define ls(p) (p<<2)\(15\text{pts}\longrightarrow55\text{pts}\)

  • 新來的標記把原標記覆寫了,但可能原標記才是有用的,(\(55\text{pts}\longrightarrow95\text{pts}\)

  • 答案可能為 \(0\),但由于 \(x=y\)\(\text{ans}\) 壓根就沒更新,還是 \(\infty\),可以通過 \(\bmod{\infty}\) 解決,(\(95\text{pts}\longrightarrow100\text{pts}\)

代碼

點擊查看代碼
const ll N=3e6+10,inf=1ll<<40;
ll n,m,q,c[N],ans=inf,lgst,lx,ly;
vector<pair<ll,ll> >tu[N];
ll fa[N],dep[N],sz[N],son[N];
ll cnt,top[N],dfn[N],rk[N];
class KillTree{
public:
	class TreeArray{
	public:
		ll b[N];
		inline void Update(ll x,ll y){while(x&&x<=n)b[x]+=y,x+=(x&-x);}
		inline ll Query(ll x){ll sum=0;while(x)sum+=b[x],x-=(x&-x);return sum;}
		inline void Build(){_for(i,1,n)Update(i,c[rk[i]]);}
	}tra;
	class SegmentTree{
	public:
		class Tree{public:ll mx,tag,sz;}tr[N<<2];
		#define sz(p) tr[p].sz
		#define mx(p) tr[p].mx
		#define ta(p) tr[p].tag
		#define ls(p) (p<<1)
		#define rs(p) (p<<1|1)
		#define l_s(p) (p<<1),l,mid
		#define r_s(p) (p<<1|1),mid+1,r
		inline void Tag(ll p,ll tg){if(tg>ta(p))ta(p)=tg,mx(p)=max(mx(p),tg);}
		inline void PushUp(ll p){mx(p)=max(mx(ls(p)),mx(rs(p)));}
		inline void PushDown(ll p){if(ta(p))Tag(ls(p),ta(p)),Tag(rs(p),ta(p)),ta(p)=0;}
		void Build(ll p,ll l,ll r){
			ta(p)=0,sz(p)=r-l+1;
			if(l==r)mx(p)=0;
			else{
				bdmd;
				Build(l_s(p)),Build(r_s(p));
				PushUp(p);
			}
			return;
		}
		void Update(ll p,ll l,ll r,ll le,ll ri,ll val){
			if(ri<l||r<le)return;
			PushDown(p);
			if(le<=l&&r<=ri)Tag(p,val);
			else{
				bdmd;
				Update(l_s(p),le,ri,val);
				Update(r_s(p),le,ri,val);
				PushUp(p);
			}
		}
		ll Query(ll p,ll l,ll r,ll x){
			if(x<l||r<x)return 0;
			PushDown(p);
			if(l==r)return mx(p);
			else{
				bdmd;
				return max(Query(l_s(p),x),Query(r_s(p),x));
			}
		}
	}set;
	void Dfs1(ll u){
		sz[u]=1,son[u]=-1;
		far(pr,tu[u]){
			ll v=pr.first,w=pr.second;
			if(v==fa[u])continue;
			fa[v]=u,dep[v]=dep[u]+1,c[v]=w;
			Dfs1(v);
			sz[u]+=sz[v];
			if(son[u]==-1||sz[son[u]]<sz[v])son[u]=v;
		}
		return;
	}
	void Dfs2(ll u,ll t){
		top[u]=t;
		dfn[u]=++cnt;
		rk[cnt]=u;
		if(son[u]==-1)return;
		Dfs2(son[u],t);
		far(pr,tu[u]){
			ll v=pr.first;
			if(v!=fa[u]&&v!=son[u])Dfs2(v,v);
		}
		return;
	}
	inline void Build(){Dfs1(1),Dfs2(1,1),tra.Build(),set.Build(1,1,n);}
	inline ll Query(ll x,ll y){
		ll num=0,tx=top[x],ty=top[y];
		while(tx!=ty){
			if(dep[tx]<dep[ty])swap(x,y),swap(tx,ty);
			num+=tra.Query(dfn[x])-tra.Query(dfn[tx]-1);
			x=fa[tx],tx=top[x];
		}
		num+=tra.Query(max(dfn[x],dfn[y]))-tra.Query(min(dfn[x],dfn[y]));
		return num;
	}
	inline void Add(ll x,ll y){
		ll tx=top[x],ty=top[y],num=Query(x,y);
		if(num>lgst)lgst=num,lx=x,ly=y;
		vector<pair<ll,ll> >e;
		while(tx!=ty){
			if(dep[tx]<dep[ty])swap(x,y),swap(tx,ty);
			e.push_back(make_pair(dfn[tx],dfn[x]));
			x=fa[tx],tx=top[x];
		}
		if(dep[x]<dep[y])swap(x,y);
		e.push_back(make_pair(dfn[y]+1,dfn[x]));
		sort(e.begin(),e.end());
		if(!e.empty()){
			ll sz=e.size()-1;
			if(e[0].first!=1)set.Update(1,1,n,1,e[0].first-1,num);
			if(e[sz].second!=n)set.Update(1,1,n,e[sz].second+1,n,num);
			_for(i,1,sz){
				if(e[i-1].second+1==e[i].first)continue;
				set.Update(1,1,n,e[i-1].second+1,e[i].first-1,num);
			}
		}
		return;
	}
	inline ll Solve(){
		while(lx!=ly){
			if(dep[lx]<dep[ly])swap(lx,ly);
			ans=min(ans,max(lgst-c[lx],set.Query(1,1,n,dfn[lx])));
			lx=fa[lx];
		}
		return ans;
	}
}kt;
namespace SOLVE{
	inline ll rnt(){
		ll x=0,w=1;char c=getchar();
		while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
		while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
		return x*w;
	}
	inline char rch(){
		char c=getchar();
		while(c<'A'||c>'Z')c=getchar();
		return c;
	}
	inline void In(){
		n=rnt(),m=rnt();
		_for(i,1,n-1){
			ll u=rnt(),v=rnt(),w=rnt();
			tu[u].push_back(make_pair(v,w));
			tu[v].push_back(make_pair(u,w));
		}
		kt.Build();
		_for(i,1,m){
			ll x=rnt(),y=rnt();
			if(x!=y)kt.Add(x,y);
		}
		printf("%lld\n",kt.Solve()%inf);
		return;
	}
}

遙遠的國度

思路

只要想清楚了就很簡單,

開個變數 \(\text{root}\) 記錄當前根,修改是常規樹剖,

然后考慮如何查詢,我們設當前查詢節點 \(u\),然后分情況討論:

  1. \(u\) 就是 \(\text{root}\):直接查詢整棵樹,

情況一

  1. \(\text{root}\)\(u\) 祖先:直接查詢以 \(u\) 為根的子樹,

image

  1. \(u\)\(\text{root}\) 祖先:直接把以 \(u\) 為根的子樹刨去,即設以 \(u\) 為根的子樹對應的區間為 \([l,r]\),則查詢區間 \([1,l-1]\)\([r+1,n]\) 的最小值,

image

代碼

點擊查看代碼
const ll N=2e5+10,inf=1ll<<40;
ll n,m,root,val[N],ans;
vector<ll>tu[N];
ll fa[N],dep[N],sz[N],son[N];
ll cnt,top[N],dfn[N],rk[N];
class KillTree{
public:
	class SegmentTree{
	public:
		class{public:ll va,ta;}tr[N<<2];
		#define va(p) tr[p].va
		#define ta(p) tr[p].ta
		#define ls(p) (p<<1)
		#define rs(p) (p<<1|1)
		#define l_s(p) (p<<1),l,mid
		#define r_s(p) (p<<1|1),mid+1,r
		inline void Tag(ll p,ll tg){va(p)=ta(p)=tg;return;}
		inline void PushUp(ll p){va(p)=min(va(ls(p)),va(rs(p)));}
		inline void PushDown(ll p){
			if(ta(p)==-1)return;
			Tag(ls(p),ta(p));
			Tag(rs(p),ta(p));
			ta(p)=-1;
			return;
		}
		void Build(ll p,ll l,ll r){
			ta(p)=-1;
			if(l==r)va(p)=val[rk[l]];
			else{
				bdmd;
				Build(l_s(p)),Build(r_s(p));
				PushUp(p);
			}
			return;
		}
		void Update(ll p,ll l,ll r,ll le,ll ri,ll val){
			if(ri<l||r<le)return;
			PushDown(p);
			if(le<=l&&r<=ri)Tag(p,val);
			else{
				bdmd;
				Update(l_s(p),le,ri,val);
				Update(r_s(p),le,ri,val);
				PushUp(p);
			}
			return;
		}
		ll Query(ll p,ll l,ll r,ll le,ll ri){
			if(ri<l||r<le)return inf;
			PushDown(p);
			if(le<=l&&r<=ri)return va(p);
			else{
				bdmd;
				return min(Query(l_s(p),le,ri),Query(r_s(p),le,ri));
			}
		}
	}tr;
	void Dfs1(ll u){
		sz[u]=1,son[u]=-1;
		far(v,tu[u]){
			if(v==fa[u])continue;
			fa[v]=u,dep[v]=dep[u]+1;
			Dfs1(v);
			sz[u]+=sz[v];
			if(son[u]==-1||sz[son[u]]<sz[v])son[u]=v;
		}
		return;
	}
	void Dfs2(ll u,ll t){
		top[u]=t;
		dfn[u]=++cnt;
		rk[cnt]=u;
		if(son[u]==-1)return;
		Dfs2(son[u],t);
		far(v,tu[u])if(v!=fa[u]&&v!=son[u])Dfs2(v,v);
		return;
	}
	inline void Build(){Dfs1(1),Dfs2(1,1),tr.Build(1,1,n);}
	inline void Update(ll x,ll y,ll z){
		ll tx=top[x],ty=top[y];
		while(tx!=ty){
			if(dep[tx]<dep[ty])swap(x,y),swap(tx,ty);
			tr.Update(1,1,n,dfn[tx],dfn[x],z);
			x=fa[tx],tx=top[x];
		}
		if(dep[x]<dep[y])swap(x,y);
		tr.Update(1,1,n,dfn[y],dfn[x],z);
	}
	inline ll Query(ll x,ll y){
		ll q=x,tx=top[x],ty=top[y],s=x;
		while(tx!=ty){
			if(dep[tx]<dep[ty])s=y,y=fa[ty],ty=top[y];
			else x=fa[tx],tx=top[x];
		}
		if(x!=y)s=son[x];
		if(dep[x]>dep[y])swap(x,y);
		if(q==root)return tr.Query(1,1,n,1,n);
		else if(x!=q)return tr.Query(1,1,n,dfn[q],dfn[q]+sz[q]-1);
		else return min(tr.Query(1,1,n,1,dfn[s]-1),tr.Query(1,1,n,dfn[s]+sz[s],n));
	}
}kt;
namespace SOLVE{
	inline ll rnt(){
		ll x=0,w=1;char c=getchar();
		while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
		while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
		return x*w;
	}
	inline void In(){
		n=rnt(),m=rnt(),root=1;
		_for(i,1,n-1){
			ll u=rnt(),v=rnt();
			tu[u].push_back(v);
			tu[v].push_back(u);
		}
		_for(i,1,n)val[i]=rnt();
		root=rnt();
		kt.Build();
		_for(i,1,m){
			ll op=rnt();
			if(op==1)root=rnt();
			else if(op==2){
				ll x=rnt(),y=rnt(),z=rnt();
				kt.Update(x,y,z);
			}
			else{
				ll x=rnt();
				printf("%lld\n",kt.Query(x,root));
			}
		}
		return;
	}
}

\[\Huge{\mathfrak{The\ End}} \]

本文來自博客園,作者:Keven-He,轉載請注明原文鏈接:https://www.cnblogs.com/Keven-He/p/KillTree.html

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/500982.html

標籤:其他

上一篇:Modbus轉Profinet網關案例 | 三菱FR-A700系列變頻器配置方法

下一篇:leetcode 48. Rotate Image 旋轉影像(Medium)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more