樹狀數組筆記整理

来源:https://www.cnblogs.com/lzyan-blog/archive/2023/01/19/17061865.html
-Advertisement-
Play Games

樹狀數組介紹 樹狀數組,顧名思義,就是樹狀的一維數組。 二叉樹同樣也可以用一維數組存儲。我們以二叉樹進行類比。 如圖所示,圖中節點的序號就是存在數組中的下標。 記父節點序號為 $p$,子節點序號為 $s$。 則有: $p$ $=$ $s$ $/$ $2$ (向下取整)。 左子節點 $s_{left} ...


樹狀數組介紹

樹狀數組,顧名思義,就是樹狀的一維數組。

二叉樹同樣也可以用一維數組存儲。我們以二叉樹進行類比。

image

如圖所示,圖中節點的序號就是存在數組中的下標

記父節點序號\(p\),子節點序號\(s\)

則有:

\(p\) \(=\) \(s\) \(/\) \(2\) (向下取整)。

左子節點 \(s_{left}\) \(=\) \(p\) \(* 2\)

右子節點 \(s_{right}\) \(=\) \(p\) \(*2+1\)

綜上可知,二叉樹能用一維數組存,是由於其父子節點間存在一定關係,以至於不需要用額外的變數來表示信息。

那類比到樹狀數組中,可以發現:

image

\(c\)數組即為樹狀數組。\(c_i\) 表示區間\(a\)\([i-lowbit(i),i]\) 的和。

ps:點我瞭解lowbit運算是什麼

同樣記父節點下標為 \(p\) ,子節點下標為 \(s\)

則有:

\(p\) \(=\) \(s\) \(+\) \(lowbit(s)\)

由這條公式亦可反推出:

\(s\) \(=\) \(p\) \(-\) \(2^i\)(\(0 \le i < p_{last}\))

這裡的 \(p_{last}\) 指的是 \(p\) 二進位表示下最後一位 \(1\) 所在的位數。

例如:\(6\) 的二進位數表示為 \(110\),則它的 \(p_{last}\)\(1\)。(這裡的位數從右往左\(0\)開始記)。

因為公式 \(1\)\(s\) 加上自身 \(lowbit(s)\) 得到 \(p\) 其過程一定會產生進位。且 \(lowbit(s)\) 一定小於 \(lowbit(p)\) ,所以可以倒推得到子節點。

由於以上關係,樹狀數組不僅可以用一維數組存。而且還衍生出了一系列用途。

樹狀數組功能

單點增加

Q:給序列中的一個數 \(a[x]\) 加上 \(y\) 。此時如何維護樹狀數組?

A:將所有包含 \(a[x]\) 的節點加上 \(y\) 即可,也就是 \(c[x]\) 和它所有的祖先節點。

ps:初始化時亦可運用此操作。

點擊查看代碼
void add(int x,int y){
	for (; x <= N;x += x&-x)  c[x] += y;
	return ;
}

動態維護首碼和

之所以說動態維護,因為用樹狀數組維護首碼和只需要 \(\log N\) 的時間複雜度。更為優秀。

Q:求 \(a\) 數組 \(a_i \sim a_x\) 的和。

A:將數 \(x\) 分成若幹個區間。

區間共同特點:若區間結尾為 \(R\),則區間長度就等於 \(lowbit(R)\),即 \(R\) 二進位分解下最小的整數次冪。

舉例:當 \(x\) = \(7\)

image

如圖所示。

區間劃分方式與樹狀數組相同。前面又提到“\(c\)數組即為樹狀數組。\(c_i\) 表示區間\(a\)\([i-lowbit(i),i]\) 的和。”

因此只需要將這幾個區間所對應的 \(c_i\) 相加。即可得到首碼和。

點擊查看代碼
int ask(int x){
	int ans = 0;
	for (; x ; x -= x & -x) ans += c[x];
	return ans;
}

例題【具體應用】

主要利用樹狀數組可以快速求首碼和的優勢,以數據範圍為下標,快速統計區間內的個數(或所需要的信息),適用於數據範圍適中(一般為 \(0 \le x \le 10^6\))且需要多次求首碼和的題目。

【例題1】 三元上升子序列

【題目分析】

對於一個數 \(x\) ,計算其作為 \(j\) 時,位置在它前面比它小的數 \(x_{min}\)、位置在它後面比它大的數 \(x_{max}\),運用乘法原理的知識可知,將\(x_{min} \times x_{max}\),即可得到 \(x\) 作為 \(j\) 時的方案數 ,枚舉所有 \(x\) ,即可得到總方案數。

【樹狀數組作用】

統計 \(x_{min}\)\(x_{max}\) 時,即可將數 \(x\) 的範圍作為樹狀數組的下標。

此時兩種操作所代表的意思分別為:

\(add(x,1)\) 表示數值為 \(x\) 的數的個數 \(+1\)

\(sum(y)\) 表示在已經掃描過的區間內,數值為從 \(1 \sim y\) 的所有數的個數。

順序掃描序列,對於數 \(x\) ,統計兩個信息。

\(r_{i,0}\) 表示位置在數 \(x\) 前面,且比它小的數。

\(r_{i,1}\) 表示位置在數 \(x\) 前面,且比它大的數。

位置在數 \(x\) 後面,且比數 \(x\) 大的數就等於:

\(所有數 - 所有位置在 x 前面比 x 小的數 - r{i,1}\)

【code】

點擊查看代碼
#include<iostream>
#include<cstdio>
#include<cmath>
#define ll long long
using namespace std;
ll tree[100005],n,num;
ll r[40005][2],a[100005];
void add(ll x,ll y){
	for(;x<=100005;x+=(x&-x)) tree[x]+=y;
}
ll sum(ll x){
	ll ans=0;
	for(;x;x-=(x&-x)) ans+=tree[x];
	return ans;
}
int main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++){
		ll x;
		scanf("%lld",&x);
		a[i]=x;
		num=max(num,x);
		add(x,1);
		r[i][0]=sum(x-1);
		r[i][1]=sum(num)-sum(x);
	}
	ll ans=0;
	for(int i=1;i<=n;i++)
		ans+=r[i][0]*(sum(num)-sum(a[i])-r[i][1]);
	cout<<ans<<endl;
	return 0;
}

【summary】

此題算是初步認識了以數值範圍為下標的樹狀數組的用法。下一大點求逆序對的思想與此相同。

【例題2】 [USACO04OPEN] MooFest G 加強版

【題目分析】

將奶牛按照音量從小到大進行排序,保證當前奶牛的音量一定最大,然後分類討論所有比當前奶牛音量小的奶牛與當前奶牛的距離(坐標比當前奶牛大的和坐標比當前奶牛小的)。兩者相加,乘上當前奶牛音量,枚舉每個奶牛,即可得到答案。

【樹狀數組作用】

定義兩個樹狀數組,都是以距離的範圍作為下標, \(c\) 數組用於統計對應距離的個數,\(t\) 數組用於表示對應距離 \(\times\) 對應 距離個數的總數,通過二者,即可快速計算距離差。

【code】

計算過程的解釋已在代碼中註釋出來。

點擊查看代碼
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
ll n,t[50005],c[50005];
struct A{
	ll v,x;
}a[50005];
bool cmp(A xx,A yy){
	if(xx.v==yy.v) return xx.x<yy.x;
	return xx.v<yy.v; 
}
void addc(ll x,ll y){
	for(;x<=50000;x+=(x&-x)) c[x]+=y;
}
void addt(ll x,ll y){
	for(;x<=50000;x+=(x&-x)) t[x]+=y;
}
ll sumc(ll x){
	ll sum=0;
	for(;x;x-=(x&-x)) sum+=c[x];
	return sum;
}
ll sumt(ll x){
	ll sum=0;
	for(;x;x-=(x&-x)) sum+=t[x];
	return sum;
}
int main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++) scanf("%lld%lld",&a[i].v,&a[i].x);
	sort(a+1,a+n+1,cmp); 
	ll ans=0,max_num=0;
	for(int i=1;i<=n;i++){
		max_num=max(max_num,a[i].x);
		//以下距離之間的比較限於所有音量比當前奶牛小的奶牛。
		//a[i].x*sumc(a[i].x-1) 表示當前奶牛的距離*距離比當前奶牛小的奶牛個數。
		//sumt(a[i].x-1) 表示所有距離比當前奶牛小的奶牛的距離和。
		//sumt(max_num)-sumt(a[i].x) 表示所有距離比當前奶牛大的奶牛的距離之和。
		//sumc(max_num)-sumc(a[i].x))*a[i].x 表示當前奶牛距離 * 距離比當前奶牛大的奶牛個數
		ans+=a[i].v*(a[i].x*sumc(a[i].x-1)-sumt(a[i].x-1)+(sumt(max_num)-sumt(a[i].x))-(sumc(max_num)-sumc(a[i].x))*a[i].x) ;
		addc(a[i].x,1);
		addt(a[i].x,a[i].x);
	}
	cout<<ans<<endl;
	return 0;
}

【summary】

這一題的重點給到題目中樹狀數組 \(t\)。主要收穫為:以數值範圍為下標的樹狀數組,能夠處理的信息不僅限於個數。

【例題3】P1972 [SDOI2009] HH的項鏈

【題意分析】

本題核心:如何判斷一個區間內的貝殼是否重覆?

當右端點 \(r\) 固定時,不論 \(l\) 取何值,對於任意一組重覆的貝殼,都可以只統計最右端的貝殼。

原因:設一組重覆貝殼中最右端的貝殼所在的位置為 \(pos_r\),那麼當 \(pos_r < l\) 時,其他貝殼也不可能算進統計中,當 $pos_r \ge l $時,無論其他貝殼是否被包括,對於區間的貢獻都只有 \(1\),因此,只計算最右端的貝殼即可。

因此,只需要將所有詢問區間按 \(r\) 從小到大排序,計算答案即可。

【樹狀數組作用】

以位置為下標,每遇到一個新的數 \(num(num \le r)\),判斷它是否重覆,如果重覆,那麼將上一個相同的數的貢獻值 \(-1\),將當前數的貢獻值 \(+1\)

對於一段區間 \([l,r]\),答案為 \(sum(r)-sum(l-1)\)

【code】

點擊查看代碼
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m,ask_r,prev,pos;
int vis[1000005],a[1000005],t[1000005],ans[1000005];
struct A{
	int l,r,num;
}ask[1000005];
bool cmp(A x,A y){
	return x.r<y.r;
}
int find(int pos){
	ask_r=ask[pos++].r;
	while(ask_r==ask[pos].r) pos++;
	return pos-1;
}
void add(int x,int y){
	for(;x<=n;x+=(x&-x)) t[x]+=y;
	return;
} 
int sum(int x){
	int su=0;
	for(;x;x-=(x&-x)) su+=t[x];
	return su;
}
void replace(){
	for(int i=ask[prev].r+1;i<=ask_r;i++){
		if(vis[a[i]]!=0) add(vis[a[i]],-1);
		add(i,1);
		vis[a[i]]=i;
	}
	for(int i=prev+1;i<=pos;i++) ans[ask[i].num]=sum(ask[i].r)-sum(ask[i].l-1);
	return;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	scanf("%d",&m);
	for(int i=1;i<=m;i++) scanf("%d%d",&ask[i].l,&ask[i].r),ask[i].num=i;
	sort(ask+1,ask+m+1,cmp);
	while(1){
		if(pos==m) break;
		prev=pos;
		pos=find(pos+1);
		replace();
	}
	for(int i=1;i<=m;i++) cout<<ans[i]<<endl;
	return 0;
}

\(summary\)

此題不再以數據範圍為下標,而是以位置為下標。對於樹狀數組的應用更加靈活。在想到以最右端的貝殼為有價值的貢獻時,對應到樹狀數組的操作就可以是上一個重覆的數的貢獻值 \(-1\),當前數的貢獻值 \(+1\)。然後用首碼和統計區間內的個數。算進一步的開闊思維。

求逆序對

本質上也是通過樹狀數組單點增加和區間求和的操作進行計算。作為一個專題單獨列出來。

桶排+樹狀數組:

1.桶排部分:

對於一個序列 \(a\) , 我們建立一個 \(cnt\) 數組,\(cnt[x]\) 表示 \(x\) 在序列 \(a\) 中出現過的次數。當 \(a_i=val\) 時,\(cnt[val]++\)

2.樹狀數組部分:

倒序掃描序列 \(a\),對於新加入的數 \(a_i\),查詢 \(cnt[1~a_i-1]\) 的首碼和,並將返回的首碼和加入答案。首碼和部分就可以用樹狀數組來維護。

操作簡單粗暴,但相當好用。

點擊查看代碼
for ( int i = n; i; i --) {
	ans += ask (a[i] - 1);
	add (a[i] , 1 );
}

【例題】

接下來通過兩道題進一步瞭解一下逆序對的考法。(不做一下真沒想到還能這樣考。)

【例題1】P2448 無盡的生命

【題意簡述】

看到題目顯而易見是求逆序對個數。

【思路分析】

看到數據範圍 \(x_i,y_i \le 2^{31}-1\)\(k \le 10^5\)。數據值域大但是個數少,且與數據之間的大小關係有關,因此考慮離散化。

離散化簡單介紹

離散化實際就是一種映射,當數據值域過大而個數有限時,可以嘗試離散化。

具體過程以此題為例。假設給出這麼一組數據

2

123456789 123456

987654321 123456

首先將所有出現過的數收集起來,存進 \(a\) 數組,併進行排序,然後再去重保存進 \(pos\) 數組當中。

接下來就可以建立映射關係。將數值大的數在 \(num\) 數組中用數值小的數代替,但各個數之間的大小關係不變,接下來交換操作先用二分答案在 \(pos\) 數組中檢索,然後通過映射在 \(num\) 數組中進行交換。

最終被交換過的數之間的逆序對在 \(num\) 數組中求即可。

被交換的數與未被交換的數之間的逆序對

考慮每個被交換的數對答案的貢獻。

\(x<y\),當 \(x\)\(y\) 交換後。

對於 \(x\) 來說, \(x \sim y\) 之間所有未被交換的數都比 \(x\) 大,形成逆序對。

對於 \(y\) 來說,\(x \sim y\) 之間所有未被交換的數都比 \(y\) 小,形成逆序對。

逆序對個數都為\(x \sim y\) 之間所有未被交換的數。

溫馨提示:以下主要為代碼實現講解,本質思想同上。

對於交換過後的 \(num\) 數組,\(num_i\) 表示的是位置 \(pos_i\) 上當前所在的數在 \(num\) 數組中對應的數。記數 \(x\) 為位置 \(pos_i\) 上當前所在的數。

\(pos_{num_i}\) 表示數 \(x\) 現在所在的位置。

\(pos_i\) 表示數 \(x\) 原來在的位置。

\(\left\vert pos_i-pos_{num_i}-1\right\vert\) 表示兩個位置間所有的數。

\(\left\vert num_i-i-1\right\vert\) 表示兩個位置間所有被交換過的數。

因此所有未被交換的數就為 \(\left\vert pos_i-pos_{num_i}-1\right\vert - \left\vert num_i-i-1\right\vert\)

【code】

點擊查看代碼
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
struct A{
	int x,y;
}a[100005];
int k,pos[200005],num[200005],cnt,len;
int t[100005];
void add(int x){
	for(;x<=len;x+=(x&-x)) t[x]+=1;
}
long long sum(int x){
	long long ans=0;
	for(;x;x-=(x&-x)) ans+=t[x];
	return ans;
}
int find(int x){
	int l=1,r=len;
	while(l<r){
		int mid=(l+r)>>1;
		if(pos[mid]<x) l=mid+1;
		else if(pos[mid]>x) r=mid-1;
		else return mid;
	}
} 
int main(){
	scanf("%d",&k);
	for(int i=1;i<=k;i++){
		scanf("%d%d",&a[i].x,&a[i].y);
		num[++cnt]=a[i].x;
		num[++cnt]=a[i].y;
	}
	sort(num+1,num+cnt+1);
	for(int i=1;i<=cnt;i++){
		if(num[i]==num[i-1]) continue;
		pos[++len]=num[i];
	}
	for(int i=1;i<=len;i++) num[i]=i;
	for(int i=1;i<=k;i++){
		int pos1=find(a[i].x);
		int pos2=find(a[i].y);
		swap(num[pos1],num[pos2]);
	}
	long long ans=0;
	for(int i=len;i>=1;i--){
		add(num[i]);
		ans+=sum(num[i]-1);
		ans+=abs(pos[num[i]]-pos[i]-1)-abs(num[i]-i-1); 
	} 
	cout<<ans<<endl;
	return 0;
}

【summary】

重點在於與未交換的數之間的求解。題目中序列的長度可以長到一個數組都存不下,但卻可以用公式求呢。

【例題2】P3531 [POI2012]LIT-Letters

【題目描述】

該題的重點在於如何從題面描述轉到求 \(逆序對\)。抓到重點:

  • 交換 \(a\) 中相鄰兩個字元,求最少的交換次數。

  • \(a,b\) 中只含大寫字母,且數據保證 \(a\) 可以變成 \(b\)

\(b\) 串中的字元進行順序編號(假設此時 \(b\) 中並沒有重覆的字母),並對應到 \(a\) 串中。

例如:

3
ABC
BCA

\(BCA\) 進行順序編號,對應到 \(ABC\) 就是 \(312\)

當序列 \(a\) 中存在數 \(a , b\),滿足 $pos_a < pos_b $ , \(a > b\)。也就是形成逆序對。

而對於我們的目標,將 \(a\) 串變成 \(b\) 串,需滿足任意數 \(a , b\),都有 \(pos_a < pos_b\) , \(a < b\)

顯然我們需要通過一定操作,令逆序對都消失,以達到目標。

由於題目中的交換為交換相鄰的數,因此只要 \(a\)\(b\) 不交換,它們之間的相對位置就不會變,也就不能達成目標。

綜上所述,最少的交換次數就是逆序對的個數。

當字母重覆時,我們要如何讓編號對應到 \(a\) 呢?

顯然逆序對個數越少越好,因此對於相同的字母,按出現的順序進行順序編號。代碼中用單向鏈表實現。

【code】

點擊查看代碼
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
int now[30],prev[30],nex[1000005];
char s[1000005],ss[1000005];
int a[1000005],t[1000005],lens;
void add(int x,int y){
	for(;x<=lens;x+=(x&-x)) t[x]+=y;
	return;
}
ll sum(int x){
	ll ans=0;
	for(;x;x-=(x&-x)) ans+=t[x];
	return ans;
}
int main(){
	scanf("%d",&lens);
	cin>>(s+1);
	cin>>(ss+1);
	for(int i=1;i<=lens;i++){
		int ch=s[i]-'A';
		if(now[ch]==0) now[ch]=i;
		nex[prev[ch]]=i;
		prev[ch]=i;
	}
	for(int i=1;i<=lens;i++){
		int ch=ss[i]-'A';
		a[now[ch]]=i;
		now[ch]=nex[now[ch]];
	}
	ll ans=0;
	for(int i=lens;i>=1;i--){
		add(a[i],1);
		ans+=sum(a[i]-1);
	}
	cout<<ans<<endl;
	return 0;
}

區間增加,單點查詢

【模板】樹狀數組2

思路剖析

相信經過上面的頭腦風暴,再來看這題已經相當簡單了。

此時主要運用到差分的思想,差分是首碼和的逆運算。

當要在區間 \([x,y]\) 加上 \(k\) 時,我們進行以下操作:

\(add(x,k) , add(y+1,-k)\)

此時對於區間求首碼和對於 \(x \sim y\),它的首碼和都為 \(k\),而到 \(y+1\) ,又變成 \(0\)。此時的首碼和正好是區間增加的數,且不會對其它數產生影響。

因此,當查詢第 \(x\) 個數時,只需要輸出:

\(a_x(第 x 個數原本的數值) + sum(x)(變化的值)\)

即可。

code

點擊查看代碼
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int a[500005],c[500005],n,m;
void add(int x,int k){
	for(;x<=n;x+=x&-x) c[x]+=k;
	return;
}
int q(int x){
	int sum=0;
	for(;x;x-=x&-x) sum+=c[x];
	return sum;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=m;i++){
		int type;
		scanf("%d",&type);
		if(type==1){
			int x,y,k;
			scanf("%d%d%d",&x,&y,&k);
			add(x,k);
			add(y+1,-k);
		}
		else{
			int x;
			scanf("%d",&x);
			cout<<a[x]+q(x)<<endl;
		}
	}
	return 0;
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 項目場景: 基於electron + Vue + node.js + express + mysql + evanpatchouli-mysql + Ant-Design-Vue,編寫一款屬於自己的輕量級MySQL資料庫界面工具。 問題列表 如何動態渲染高度自定義的Ant Design Table? ...
  • 概念 Array 數組是有序的元素序列。 語法 new Array(length) new Array(element1) new Array(element1, element2) new Array(element1, element2, element3) new Array(element1 ...
  • 最近看了一本書《解構-領域驅動設計》,書中提出了領域驅動設計統一過程(DDDRUP),它指明瞭實踐 DDD 的具體步驟,並很好地串聯了各種概念、模式和思想。因此,我對書本內容做了梳理、簡化,融入自己的理解,並結合之前閱讀的書籍以及實踐經驗,最終形成這篇文章。希望可以幫助大伙理順 DDD 的各種概念、... ...
  • 2023-01-19 一、@PathVariable註解基本使用 1、獲取URL中占位符 2、占位符語法:{} 3、實例代碼: @RequestMapping("testPathVariable/{empId}") public String testPathVariable(@PathVariab ...
  • Spring管理Bean-IOC-04 3.基於註解配置bean 3.1基本使用 3.1.1說明 基本說明:基於註解的方式配置bean,主要是項目開發中的組件,比如Controller,Service和Dao 組件的註解形式有: @Component 表示當前註解標識的是一個組件 @Controll ...
  • 1.在運行http時,報錯:panic: listen tcp: address 11111: missing port in address, 初始 代碼如下 func HelloWordHander(w http.ResponseWriter, r *http.Request) { /** 具體 ...
  • SpringBoot2(一)SpringBoot入門程式 在整合SSM框架時,需要大量的配置文件,SpringBoot可以簡化這些配置,提高開發效率。並且Spring內置了Tomcat、Jetty、Undertow容器。 搭建SpringBoot2的環境 方法一:通過官網生成項目 進入官網https ...
  • 在springboot項目中,dubbo消費者在設置dubbo超時時間時,可以在application.yml里設置屬性 dubbo.consumer.timeout。這是服務級。也可以在@Reference註解上給timeout屬性賦值,來指定特定介面的超時時間。 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...