YbtOJ 「数据结构」 第4章 线段树

线段树

维护区间的一棵树 四个基本操作:单点查询 单点修改 区间查询 区间修改

父节点维护的是子节点的值合并后的总和(区间加和 区间最大值等等)

每次在递归下去的时候 先判断区间 再下传标记 再递归寻找下一个区间

注意两个儿子是p<<1和p<<1|1 两个区间是[l,mid]和[mid+1,r]

一条链上只可能有一个节点拥有懒标记

如果修改区间覆盖了整个范围,就只需要打一个懒标记就好,打懒标记的同时需要更新这个被打标记的点的各项数值

等下一次问询到这个节点的时候,p节点的值已经更新好了,只需要把懒标记下传下去,子节点更新就可以了

A. 【例题1】求区间和

不需要打懒标记的单点修改区间查询 代码略

B. 【例题2】区间查改

代码略 详见洛谷【模板】线段树 1

C. 【例题3】公园遛狗

需要维护给定区间范围内的 一段连续小区间的区间和最大值

经典操作:设置prel表示从左向右的最大和 prer表示从右向左的最大和 res表示区间最大和 向上维护即可

特别注意res的转移:t[p].res=max(max(t[ls].res,t[rs].res),t[ls].prer+t[rs].prel); 是两个子节点的答案和两个子节点的中间合并段取max

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define endl '\n'
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 4e6 + 5;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , a[N];

struct node { int sum , prel , prer , res; } t[N];

void up ( int p )
{
	t[p].sum = t[ls].sum + t[rs].sum;
	t[p].prel = max ( t[ls].sum + t[rs].prel , t[ls].prel );
	t[p].prer = max ( t[rs].sum + t[ls].prer , t[rs].prer );
	t[p].res = max ( max ( t[ls].res , t[rs].res ) , t[ls].prer + t[rs].prel );
}

void build ( int p , int l , int r )
{
	if ( l == r ) return t[p] = { a[l] , a[l] , a[l] , a[l] } , void();
	build ( lson ) , build ( rson ) , up(p);
}

void upd ( int p , int l , int r , int x , int val )
{
	if ( l == r ) return t[p] = { val , val , val , val } , void();
	if ( x <= mid ) upd ( lson , x , val );
	else upd ( rson , x , val );
	up(p);
}

node query ( int p , int l , int r , int x , int y )
{
	if ( x <= l && r <= y ) return t[p];
	if ( x >= mid + 1 ) return query ( rson , x , y );
	else if ( y <= mid ) return query ( lson , x , y );
	else 
	{
		node temp , u = query ( lson , x , y ) , v = query ( rson , x , y );
		temp.sum = u.sum + v.sum;
		temp.prel = max ( u.sum + v.prel , u.prel );
		temp.prer = max ( v.sum + u.prer , v.prer );
		temp.res = max ( max ( u.res , v.res ) , u.prer + v.prel );
		return temp;
	}
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
	build ( 1 , 1 , n );
	for ( int i = 1 ; i <= m ; i ++ )
	{
		int op = read() , x = read() , y = read();
		if ( op == 1 )
		{
			if ( x > y ) swap ( x , y );
			cout << query ( 1 , 1 , n , x , y ).res << endl; 
		}
		else upd ( 1 , 1 , n , x , y );
	}
	return 0;
}

D. 【例题4】维护序列

洛谷线段树2模板

上传的时候子节点的add需要乘上父亲节点的mul

注意:建树时无论是否是l=r mul都需要设置为1

懒标记下传时 低级别懒标记需要累加高级别懒标记的影响

赋值懒标记时 为高级别懒标记赋值的时候需要同时积累低级别懒标记的值

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long 
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 4e6 + 5

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , mod , a[N];

struct node { int sum , mul , add; } t[N];
void up ( int p ) { t[p].sum = ( t[ls].sum + t[rs].sum ) % mod; }
void down ( int p , int l , int r )
{
	t[ls].sum = ( t[ls].sum * t[p].mul + ( mid - l + 1 ) * t[p].add ) % mod; 
	t[rs].sum = ( t[rs].sum * t[p].mul + ( r - mid ) * t[p].add ) % mod;
	
	t[ls].mul = ( t[ls].mul * t[p].mul ) % mod;
	t[rs].mul = ( t[rs].mul * t[p].mul ) % mod;
	
	t[ls].add = ( t[ls].add * t[p].mul + t[p].add ) % mod;
	t[rs].add = ( t[rs].add * t[p].mul + t[p].add ) % mod;
	
	t[p].mul = 1 , t[p].add = 0;
}

void build ( int p , int l , int r )
{
	t[p].mul = 1;
	if ( l == r ) return t[p] = { a[l] , 1 , 0 } , void();
	build ( lson ) , build ( rson ) , up(p);
}

void mul ( int p , int l , int r , int x , int y , int val )
{
	if ( x <= l && r <= y ) return t[p].sum = ( t[p].sum * val ) % mod , t[p].mul = ( t[p].mul * val ) % mod , t[p].add = ( t[p].add * val ) % mod , void();
	down ( p , l , r );
	if ( x <= mid ) mul ( lson , x , y , val );
	if ( mid + 1 <= y ) mul ( rson , x , y , val );
	up(p);
}
	

void add ( int p , int l , int r , int x , int y , int val )
{
	if ( x <= l && r <= y ) return t[p].sum = ( t[p].sum + ( r - l + 1 ) * val % mod ) % mod , t[p].add = ( t[p].add + val ) % mod , void();
	down ( p , l , r );
	if ( x <= mid ) add ( lson , x , y , val );
	if ( mid + 1 <= y ) add ( rson , x , y , val );
	up(p);
}


int query ( int p , int l , int r , int x , int y )
{
	if ( x <= l && r <= y ) return t[p].sum;
	down ( p , l , r );
	int res = 0;
	if ( x <= mid ) res = ( res + query ( lson , x , y ) % mod ) % mod; 
	if ( mid + 1 <= y ) res = ( res + query ( rson , x , y ) % mod ) % mod;
	return res;
}


signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , mod = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
	build ( 1 , 1 , n ) , m = read();
	for ( int i = 1 ; i <= m ; i ++ ) 
	{
		int op = read() , l = read() , r = read() , val;
		if ( op == 1 ) val = read() , mul ( 1 , 1 , n , l , r , val );
		if ( op == 2 ) val = read() , add ( 1 , 1 , n , l , r , val );
		if ( op == 3 ) cout << query ( 1 , 1 , n , l , r ) << endl;
	}
	return 0;
}

E. 【例题5】字符串排序

我们设置第i个小写字母在排序区间l,r中出现次数为a[i]a[i]可以用线段树求出(建26棵线段树)

排序时等价于在区间[l,r]中 将[l,l+a[1]1]赋值为a[l+a[1],l+a[1]+a[2]1]赋值为b 以此类推 赋值操作可以用线段树区间修改来实现

注意需要特判l<l+a[1]1的情况(因为可能这个点没有值)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 1e6 + 5;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , mod , a[N] , tmp[N];

string s;

int t[N][27] , lazy[N];

void up ( int p ) { for ( int i = 1 ; i <= 26 ; i ++ ) t[p][i] = t[ls][i] + t[rs][i]; }

void changedown ( int p , int l , int r , int val )
{
	lazy[p] = val;
	for ( int i = 1 ; i <= 26 ; i ++ ) t[p][i] = 0;
	t[p][val] = r - l + 1;
}

void down ( int p , int l , int r )
{
	if ( lazy[p] )
	{
		changedown ( lson , lazy[p] );
		changedown ( rson , lazy[p] );
		lazy[p] = 0;
	}
}

char aaa;

void build ( int p , int l , int r )
{
	if ( l == r ) return t[p][s[l]-'a'+1] = 1 , void();
	build ( lson ) , build ( rson ) , up(p);
}

int query ( int id , int p , int l , int r , int x , int y )
{
	if ( x <= l && r <= y ) return t[p][id];
	down ( p , l , r );
	int ans = 0;
	if ( x <= mid ) ans += query ( id , lson , x , y );
	if ( mid + 1 <= y ) ans += query ( id , rson , x , y );
	return ans;	
}

void change ( int id , int p , int l , int r , int x , int y )
{
	if ( x > y ) return;//需要特判
	if ( x <= l && r <= y ) return changedown ( p , l , r , id ) , void();
	down ( p , l , r );
	if ( x <= mid ) change ( id , lson , x , y );
	if ( mid + 1 <= y ) change ( id , rson , x , y );
	up(p);
}

void print ( int p , int l , int r )
{
	if ( l == r ) 
	{
		for ( int i = 1 ; i <= 26 ; i ++ )
			if ( t[p][i] ) cout << (char)(i+'a'-1);
		return;
	}
	down ( p , l , r ) , print ( lson ) , print ( rson );
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	cin >> s , s = " " + s;
	build ( 1 , 1 , n );
	for ( int i = 1 ; i <= m ; i ++ ) 
	{
		int l = read() , r = read() , op = read();
		for ( int j = 1 ; j <= 26 ; j ++ ) tmp[j] = query ( j , 1 , 1 , n , l , r );
		if ( op )
			for ( int j = 1 ; j <= 26 ; j ++ )
			{
				change ( j , 1 , 1 , n , l , l + tmp[j] - 1 );
				l += tmp[j];
				if ( l > r ) continue;
			}
		else 
			for ( int j = 26 ; j; j -- )
			{
				change ( j , 1 , 1 , n , l , l + tmp[j] - 1 );
				l += tmp[j];
				if ( l > r ) continue;
			}
	}
	print ( 1 , 1 , n );
	return 0;
}

F. 1.取模问题

不需要懒标记 如果模数大于这段区间的最大值 那么直接跳过 其他和线段树维护一样

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define endl '\n'
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 1e6 + 5;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , a[N];

struct node { int val , maxx; } t[N];

void up ( int p ) { t[p].maxx = max ( t[ls].maxx , t[rs].maxx ) , t[p].val = t[ls].val + t[rs].val; }

void build ( int p , int l , int r )
{
	if ( l == r ) return t[p] = { a[l] , a[l] } , void();
	build ( lson ) , build ( rson ) , up(p);
}

void upd ( int p , int l , int r , int x , int val )
{
	if ( l == r ) return t[p] = { val , val } , void();
	if ( x <= mid ) upd ( lson , x , val );
	else upd ( rson , x , val );
	up(p);
}

void mod ( int p , int l , int r , int x , int y , int val )
{
	if ( val > t[p].maxx ) return;
	if ( l == r ) return t[p].val %= val , t[p].maxx = t[p].val , void();
	if ( x <= mid ) mod ( lson , x , y , val );
	if ( mid + 1 <= y ) mod ( rson , x , y , val );
	up(p);
}

int query ( int p , int l , int r , int x , int y )
{
	if ( x <= l && r <= y ) return t[p].val;
	int res = 0;
	if ( x <= mid ) res += query ( lson , x , y );
	if ( mid + 1 <= y ) res += query ( rson , x , y );
	return res;
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
	build ( 1 , 1 , n );
	for ( int i = 1 , val ; i <= m ; i ++ )
	{
		int op = read() , x = read() , y = read();
		if ( op == 1 ) cout << query ( 1 , 1 , n , x , y ) << endl;
		if ( op == 2 ) val = read() , mod ( 1 , 1 , n , x , y , val );
		if ( op == 3 ) upd ( 1 , 1 , n , x , y );
	}
	return 0;
}

G. 2.魔法传输

我们维护一个差分数组 对于一个操作x,y 我们将x,y区间内的所有值都加一 再在y+1的点加上yx+1来撤销这个操作对于后面的影响

对于查询 则查询该点前缀和即可

注意调用下传函数

需要特判x>y

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define endl '\n'
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 1e6 + 5;
const int mod = 1000000007;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , a[N];

struct node { int sum , add; } t[N];

void up ( int p ) { t[p].sum = ( t[ls].sum + t[rs].sum ) % mod; }

void changedown ( int p , int l , int r , int val )
{
	t[p].sum = ( t[p].sum + ( r - l + 1 ) * val ) % mod;
	t[p].add = ( t[p].add + val ) % mod;
}

void down ( int p , int l , int r )
{
	if ( t[p].add )
	{
		changedown ( lson , t[p].add );
		changedown ( rson , t[p].add );
		t[p].add = 0;
	}
}

void upd ( int p , int l , int r , int x , int y , int val )
{
	if ( x <= l && r <= y ) return changedown ( p , l , r , val ) , void();
	down ( p , l , r );
	if ( x <= mid ) upd ( lson , x , y , val );
	if ( mid + 1 <= y ) upd ( rson , x , y , val );
	up(p);
}

int query ( int p , int l , int r , int x , int y )
{
	if ( x <= l && r <= y ) return t[p].sum;
	down ( p , l , r );
	int res = 0;
	if ( x <= mid ) res = ( res + query ( lson , x , y ) ) % mod;
	if ( mid + 1 <= y ) res = ( res + query ( rson , x , y ) ) % mod;
	return res;
}


char op;
signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 , x , y ; i <= m ; i ++ )
	{
		cin >> op;
		if ( op == 'C' ) x = read() , y = read() , upd ( 1 , 1 , n , x , y , 1 ) , upd ( 1 , 1 , n , y + 1 , y + 1 , - ( y - x + 1 ) ); 
		if ( op == 'Q' ) x = read() , cout << query ( 1 , 1 , n , 1 , x ) << endl;
	}
	return 0;
}

H. 3.队伍整理

对于n+m个节点建树

统计的时候标记所有有值的位置并标记 统计vis数组的前缀和 在mn个长度为n的段中选取人数最多的一个 并把其他的人移动进来即可

注意坑点:查询的时候上限是n+m而不是n

需要特判x>y

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 2e5 + 5;
const int inf = 1e9;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , flag , a[N] , b[10000010] , vis[N] , sum[N] , ans , t[N<<2];

void up ( int p ) { t[p] = min ( t[ls] , t[rs] ); }

void build ( int p , int l , int r )
{
	if ( l == r ) return t[p] = a[l] , void();
	build ( lson ) , build ( rson ) , up(p);
}

void upd ( int p , int l , int r , int x , int val )
{
	if ( l == r ) return t[p] = val , void();
	if ( x <= mid ) upd ( lson , x , val );
	else upd ( rson , x , val );
	up(p);
}

int query ( int p , int l , int r , int x , int y )
{
	if ( y < x ) return inf;
	if ( x <= l && r <= y ) return t[p];
	int res = inf;
	if ( x <= mid ) res = min ( res , query ( lson , x , y ) );
	if ( mid + 1 <= y ) res = min ( res , query ( rson , x , y ) );
	return res;
}

char op;
signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= m + n ; i ++ ) a[i] = inf;
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , b[a[i]] = i;
	build ( 1 , 1 , n + m );
	flag = n;
	for ( int i = 1 , x ; i <= m ; i ++ )
	{
		cin >> op , x = read();
		if ( op == 'A' )
		{
			if ( !b[x] ) { cout << -1 << endl; continue; }
			int temp = query ( 1 , 1 , n + m , 1 , b[x] - 1 );
			if ( temp == inf ) temp = -1;
			cout << temp << endl;
		}
		else 
		{
			upd ( 1 , 1 , n + m , b[x] , inf );
			b[x] = ++flag;
			upd ( 1 , 1 , n + m , b[x] , x );
		}
	}
	for ( int i = 1 ; i <= n ; i ++ ) vis[b[a[i]]] = 1;
	for ( int i = 1 ; i <= n + m ; i ++ ) sum[i] = sum[i-1] + vis[i];
	for ( int i = n ; i <= n + m ; i ++ ) ans = max ( ans , sum[i] - sum[i-n] );
	cout << n - ans << endl;
	return 0;
}

I. 4.和或异或

考虑用线段树直接模拟这n次操作 那么我们设置st数组 st1是子节点或上来 st0是异或上来即可 修改操作直接用单点修改实现

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 4e5 + 5;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , q , t[N<<2] , st[N] , a[N];//st为1是子节点或上来 st为0是异或上来


void up ( int p )
{
	st[p] = st[ls] ^ 1;
	if ( !st[p] ) t[p] = t[ls] ^ t[rs];
	else t[p] = t[ls] | t[rs];
}

void build ( int p , int l , int r )
{
	if ( l == r ) return t[p] = a[l] , void();
	build ( lson ) , build ( rson ) , up(p);
}

void upd ( int p , int l , int r , int x , int val )
{
	if ( l == r ) return t[p] = val , void();
	if ( x <= mid ) upd ( lson , x , val );
	else upd ( rson , x , val );
	up(p);
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , q = read();
	for ( int i = 1 ; i <= 1 << n ; i ++ ) a[i] = read();
	build ( 1 , 1 , 1 << n );
	for ( int i = 1 ; i <= q ; i ++ )
	{
		int x = read() , y = read();
		upd ( 1 , 1 , 1 << n , x , y );
		cout << t[1] << endl;
	}
	return 0;
}

J. 5.括号匹配

我们设置nol,nor,res区间内有多少个不能匹配的左括号/右括号 整个区间匹配括号对数

三个转移更新见代码 注意结构体需要初始化{0,0,0}

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 4e5 + 5;
		
int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m;
string s;

struct node { int nol , nor , res; } t[N<<2];//区间内有多少个不能匹配的左括号/右括号 整个区间匹配括号对数

node merge ( node x , node y )
{
	node res = { 0 , 0 , 0 };
	res.nol = x.nol + y.nol - min ( x.nol , y.nor );
	res.nor = x.nor + y.nor - min ( x.nol , y.nor );
	res.res = x.res + y.res + min ( x.nol , y.nor );
	return res;
}

void up ( int p ) { t[p] = merge ( t[ls] , t[rs] ); }

void build ( int p , int l , int r )
{
	if ( l == r ) return s[l] == '(' ? t[p].nol = 1 : t[p].nor = 1 , void();
	build ( lson ) , build ( rson ) , up(p);
}

node query ( int p , int l , int r , int x , int y )
{
	if ( x <= l && r <= y ) return t[p];
	node ans = { 0 , 0 , 0 };
	if ( x <= mid ) ans = merge ( ans , query ( lson , x , y ) );
	if ( mid + 1 <= y ) ans = merge ( ans , query ( rson , x , y ) );
	return ans;
}


signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	cin >> s; s = " " + s;
	build ( 1 , 1 , n );
	for ( int i = 1 ; i <= m ; i ++ )
	{
		int x = read() , y = read();
		node temp = query ( 1 , 1 , n , x , y );
		cout << temp.res * 2 << endl;
	}
	return 0;
}
posted @   Echo_Long  阅读(93)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示