2023ACM暑假训练day 10 树上问题
DAY 10 树上问题
训练情况简介
2023-07-08 09:51:05 星期六
训练地址:传送门
早上:H
下午:IA
晚上:DE
后补:F
树的dfs序+树的重心+LCA
H 题 dfs序+线段树维护区间大乘积(取log)
I 题 dfs序+线段树维护区间和(维护区间和这个可以推,仔细想想就行)
A 题 树的重心,怎么通过删边增边使得树的重心唯一
D 题 LCA最近公共祖先模板题
E 题 求树上一点移动向另一点移动的最终停止点,移动距离有限制,路径要求最短
F 题 多次求包含指定边的最小生成树的边权之和。蛮综合的一题,非常值得尝试
H 题
cf E. Filthy Rich Trees
题意:
给你一棵树,所有节点的初始权值为1。进行q次操作,每次操作要么将节点x的权值修改为y,要么问你,x的子树的权值之积和y的子树的权值之积的比值,比值大于1e9是输出1e9
思路:
找任意节点的子树,考虑dfs序,将子树变为区间,再利用线段树维护乘积,直接维护乘积数字过大,所以维护乘积的log,输出时取10的幂次即可
有意思,有空可以再试试
//>>>Qiansui #include<map> #include<set> #include<list> #include<stack> #include<cmath> #include<queue> #include<deque> #include<cstdio> #include<string> #include<vector> #include<utility> #include<iomanip> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #include<functional> #define ll long long #define ull unsigned long long #define mem(x,y) memset(x,y,sizeof(x)) #define debug(x) cout << #x << " = " << x << endl #define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl //#define int long long inline ll read() { ll x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();} return x*f; } using namespace std; typedef pair<int,int> pii; typedef pair<ll,ll> pll; typedef pair<ull,ull> pull; typedef pair<double,double> pdd; /* 呜呜呜,写了半天,发现题目看错了。。。 区间乘积?线段树维护吧 但是维护乘积,会爆吧,取个对数?对10取对数 线段树维护log区间和 ! update的时候没取log,可能是wa的原因 solve里面query下标写错了,,, 小问题蛮多的哈,下次注意! */ const int maxm=3e5+5,inf=0x3f3f3f3f,mod=998244353; int n,q,head[maxm],cnt=1,l[maxm],r[maxm],xu=0; double seg[maxm<<2]; struct node{ int to,next; }p[maxm<<1]; void add_edge(int a,int b){ p[cnt].to=b; p[cnt].next=head[a]; head[a]=cnt++; return ; } void dfs(int x,int fa){ l[x]=++xu; for(int i=head[x];i;i=p[i].next){ if(p[i].to==fa) continue; dfs(p[i].to,x); } r[x]=xu; return ; } int ls(int p) { return p<<1; } int rs(int p) { return p<<1|1; } void push_up(int p){ seg[p]=seg[ls(p)]+seg[rs(p)]; return ; } void build(int p,int pl,int pr){ if(pl==pr){ seg[p]=0;//lg(1)=0 因为初始v=1 return ; } int mid=(pl+pr)>>1; build(ls(p),pl,mid); build(rs(p),mid+1,pr); return ; } void update(int pos,double k,int p,int pl,int pr){ if(pl==pr){ seg[p]=k; return ; } int mid=(pl+pr)>>1; if(pos<=mid) update(pos,k,ls(p),pl,mid); else update(pos,k,rs(p),mid+1,pr); push_up(p); return ; } double query(int l,int r,int p,int pl,int pr){ if(l<=pl&&pr<=r){ return seg[p]; } int mid=(pl+pr)>>1; double res=0; if(l<=mid) res+=query(l,r,ls(p),pl,mid); if(mid<r) res+=query(l,r,rs(p),mid+1,pr); return res; } void solve(){ cin>>n; int a,b; for(int i=0;i<n-1;++i){ cin>>a>>b; add_edge(a,b); add_edge(b,a); } dfs(1,0); build(1,1,n); cin>>q; int op; while(q--){ cin>>op; if(op==1){//update int x;double y; cin>>x>>y; update(l[x],log10(y),1,1,n); }else{//compare int x,y; cin>>x>>y; double xx,yy; xx=query(l[x],r[x],1,1,n); yy=query(l[y],r[y],1,1,n); double d=xx-yy; if(d>=9.0) cout<<"1000000000\n"; else cout<<fixed<<setprecision(10)<<pow(10,d)<<'\n'; } } return ; } signed main(){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); int _=1; // cin>>_; while(_--){ solve(); } return 0; }
I 题
cf F. The Lorax
题意:
给你一棵树,n个节点,n-1条边,所有节点均包含两个属性:seed和pot,初始两个属性均为0。共q次操作,每次操作要么让你给a节点加c个seed,给b节点加c个pot;或者问你对于某一边的两个端点a和b,在满足所有seed与pot一一匹配且匹配总距离最短的情况下(任意两节点之间的距离就是最短的路径长度),问你必须经过当前边的匹配对数。
思路:
就是说,如果对于两个点,以直接将他俩连接的边作为分界线,将给定的树划分为两棵树,左树右树里面均有seed和pot。如果说左树里面的seed与pot已经匹配上了,没有剩余,那么就不需要跨越边界去另一棵树匹配;反之,如果匹配不上,那么就需要跨越边界去寻找,而求的就是这个跨越边界的seed和pot的对数。
英文题面还是有点吃力~~~
其实题目并不难,分析之后我们可以发现,就是在单一的一边找abs(seed-pot)。对吧!因为是成对添加的,所以匹配不上就要跨越,而匹配不上就是答案。为此,我们可以令+1个seed为节点当前权值+1,+1个pot为当前权值-1,那么,当询问时,我们只需要统计一棵子树的权值和即可。
这可以利用dfs序+线段树快速求解。
那么最后一个问题,子树怎么确定呢?或者说,该怎么求和呢?对于给定的边端点a和b,我们可以知道,深度深的那个端点,也就是dfs序后的端点,被划分为一棵子树,剩下的作为另一棵子树,那么我们的深的那棵子树求权值和即可。
详见代码:
//>>>Qiansui #include<map> #include<set> #include<list> #include<stack> #include<cmath> #include<queue> #include<deque> #include<cstdio> #include<string> #include<vector> #include<utility> #include<iomanip> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #include<functional> #define ll long long #define ull unsigned long long #define mem(x,y) memset(x,y,sizeof(x)) #define debug(x) cout << #x << " = " << x << endl #define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl //#define int long long inline ll read() { ll x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();} return x*f; } using namespace std; typedef pair<int,int> pii; typedef pair<ll,ll> pll; typedef pair<ull,ull> pull; typedef pair<double,double> pdd; /* 不知道错在什么地方了,我感觉是想法错了 其实仅考虑就是给你的ab一定是一条边的两个点 所以,你找dfs序靠后的位置,找它的子树里面的未匹配数就行 另外,x~1e8别忘了long long,也别忘了相关的函数返回值也要改long long */ const int maxm=1e5+5,inf=0x3f3f3f3f,mod=998244353; int n,q,cnt,head[maxm],l[maxm],r[maxm]; ll seg[maxm<<2]; struct edge{ int to,next; }p[maxm<<1]; void pre(){ cnt=1; for(int i=0;i<=n;++i) head[i]=0; return ; } void add_edge(int a,int b){ p[cnt].to=b; p[cnt].next=head[a]; head[a]=cnt++; return ; } void dfs(int x,int fa){ l[x]=++cnt; for(int i=head[x];i;i=p[i].next){ if(p[i].to==fa) continue; dfs(p[i].to,x); } r[x]=cnt; return ; } int ls(int p) { return p<<1; } int rs(int p) { return p<<1|1; } void push_up(int p){ seg[p]=seg[ls(p)]+seg[rs(p)]; return ; } void build(int p,int pl,int pr){ if(pl==pr){ seg[p]=0; return ; } int mid=(pl+pr)>>1; build(ls(p),pl,mid); build(rs(p),mid+1,pr); push_up(p); return ; } void update(int pos,ll k,int c,int p,int pl,int pr){ if(pl==pr){ if(c==0) seg[p]+=k; else seg[p]-=k; return ; } int mid=(pl+pr)>>1; if(pos<=mid) update(pos,k,c,ls(p),pl,mid); else update(pos,k,c,rs(p),mid+1,pr); push_up(p); return ; } ll query(int l,int r,int p,int pl,int pr){ if(l<=pl&&pr<=r) return seg[p]; ll mid=(pl+pr)>>1,ans=0; if(l<=mid) ans+=query(l,r,ls(p),pl,mid); if(mid<r) ans+=query(l,r,rs(p),mid+1,pr); return ans; } void solve(){ cin>>n>>q; pre(); int a,b; for(int i=0;i<n-1;++i){ cin>>a>>b; add_edge(a,b); add_edge(b,a); } cnt=0; dfs(1,0); build(1,1,n); ll c; while(q--){ cin>>a>>b>>c; if(c==0){//query int y; if(l[a]>l[b]) y=a; else y=b; cout<<abs(query(l[y],r[y],1,1,n))<<'\n'; }else{//add update(l[a],c,0,1,1,n); update(l[b],c,1,1,1,n); } } return ; } signed main(){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); int _=1; cin>>_; while(_--){ solve(); } return 0; }
A 题
cf 1406 C. Link Cut Centroids
题意:
给你一棵树,n个顶点,n-1条边。问你通过先删边再增边的操作,使得最后的树的重心唯一,增删的边可以相同
思路:
对于只有唯一重心的树,随意删增同一条边就行。
对于有2个重心的树,这两个重心一定由一条边相连,则我们只需要改变一个重心的最大子树节点数即可,这样就能使得重心唯一。选一个重心的一个叶子连在另一个重心上即可。自己的代码是将一个重心的一个子树连在另一个重心上,不知是否有反例
下为代码:
//>>>Qiansui #include<map> #include<set> #include<list> #include<stack> #include<cmath> #include<queue> #include<deque> #include<cstdio> #include<string> #include<vector> #include<utility> #include<iomanip> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #include<functional> #define ll long long #define ull unsigned long long #define mem(x,y) memset(x,y,sizeof(x)) #define debug(x) cout << #x << " = " << x << endl #define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl //#define int long long inline ll read() { ll x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();} return x*f; } using namespace std; typedef pair<int,int> pii; typedef pair<ll,ll> pll; typedef pair<ull,ull> pull; typedef pair<double,double> pdd; /* 通过加边删边的操作使得树的重心唯一? */ const int maxm=1e5+5,inf=0x3f3f3f3f,mod=998244353; int n,head[maxm],cnt,d[maxm],len; vector<int> e; struct node{ int to,next; }p[maxm<<1]; void add_edge(int a,int b){ p[cnt].to=b; p[cnt].next=head[a]; head[a]=cnt++; return ; } int dfs(int x,int fa){ d[x]=1; int t,Max=0; for(int i=head[x];i;i=p[i].next){ if(p[i].to==fa) continue; t=dfs(p[i].to,x); d[x]+=t; Max=max(Max,t); } Max=max(Max,n-d[x]); if(Max<len){ len=Max; e.clear(); e.push_back(x); }else if(Max==len){ e.push_back(x); } return d[x]; } void solve(){ mem(head,0); mem(d,0); e.clear(); cnt=1; cin>>n; len=n+1; int a,b; for(int i=1;i<n;++i){ cin>>a>>b; add_edge(a,b); add_edge(b,a); } dfs(1,0); if(e.size()==1){//一个重心,随便增删 int t=e[0],u=p[head[t]].to; cout<<t<<" "<<u<<'\n' <<t<<' '<<u<<'\n'; }else{//两个重心,改变一个重心的最大子树节点数,但有没有反例? int x=e[0],y=e[1],z; for(int i=head[x];i;i=p[i].next){ if(p[i].to==y) continue; else{ z=p[i].to; break; } } cout<<z<<' '<<x<<'\n' <<z<<' '<<y<<'\n'; } return ; } signed main(){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); int _=1; cin>>_; while(_--){ solve(); } return 0; }
D 题
洛谷 P3379 【模板】最近公共祖先(LCA)
题意:
求树上指定两个点直接最近的公共祖先
思路:
LCA模板题,算法过程详见自己的摘记
下为代码:
//>>>Qiansui #include<map> #include<set> #include<list> #include<stack> #include<cmath> #include<queue> #include<deque> #include<cstdio> #include<string> #include<vector> #include<utility> #include<iomanip> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #include<functional> #define ll long long #define ull unsigned long long #define mem(x,y) memset(x,y,sizeof(x)) #define debug(x) cout << #x << " = " << x << endl #define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl //#define int long long inline ll read() { ll x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();} return x*f; } using namespace std; typedef pair<int,int> pii; typedef pair<ll,ll> pll; typedef pair<ull,ull> pull; typedef pair<double,double> pdd; /* 最近公共祖先模板题 利用深搜构造lca数组 */ const int maxm=5e5+5,inf=0x3f3f3f3f,mod=998244353,N=20; int n,m,s,cnt=1,head[maxm],dep[maxm],fa[maxm][N+1]; struct node{ int to,next; }p[maxm<<1]; void add_edge(int a,int b){ p[cnt].to=b; p[cnt].next=head[a]; head[a]=cnt++; return ; } void dfs(int x,int f){ dep[x]=dep[f]+1; fa[x][0]=f; for(int i=1;i<=N;++i){ fa[x][i]=fa[fa[x][i-1]][i-1]; } for(int i=head[x];i;i=p[i].next){ if(p[i].to!=f) dfs(p[i].to,x); } return; } int lca(int u,int v){ if(dep[u]<dep[v]) swap(u,v); for(int i=N;i>=0;--i){ if(dep[fa[u][i]]>=dep[v]){ u=fa[u][i]; } } if(u==v) return v; for(int i=N;i>=0;--i){ if(fa[u][i]!=fa[v][i]){ u=fa[u][i]; v=fa[v][i]; } } return fa[v][0]; } void solve(){ cin>>n>>m>>s; int a,b; for(int i=1;i<n;++i){ cin>>a>>b; add_edge(a,b); add_edge(b,a); } dfs(s,0); while(m--){ cin>>a>>b; cout<<lca(a,b)<<'\n'; } return ; } signed main(){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); int _=1; // cin>>_; while(_--){ solve(); } return 0; }
E 题
cf C. Sloth Naptime
题意
就是给你一棵树,一共q次询问,每次询主人的树懒位于节点a,它具有c的能量最多能跨越c条边,主人想让其到达节点b。如果可以抵达节点b,树懒就会待在节点b;如果不能到达,那么树懒会待在离节点b最近的地方。每次询问让你输出树懒最终停留的位置。
思路
考虑a和b的最近公共祖先m,最短路径长为
1.
2.
当
而具体的位置,可以利用fa数组求解
//>>>Qiansui #include<map> #include<set> #include<list> #include<stack> #include<cmath> #include<queue> #include<deque> #include<cstdio> #include<string> #include<vector> #include<utility> #include<iomanip> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #include<functional> #define ll long long #define ull unsigned long long #define mem(x,y) memset(x,y,sizeof(x)) #define debug(x) cout << #x << " = " << x << endl #define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl //#define int long long inline ll read() { ll x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();} return x*f; } using namespace std; typedef pair<int,int> pii; typedef pair<ll,ll> pll; typedef pair<ull,ull> pull; typedef pair<double,double> pdd; /* */ const int maxm=3e5+5,inf=0x3f3f3f3f,mod=998244353,N=20; int n,q,cnt=1,head[maxm],dep[maxm],fa[maxm][N+1]; struct node{ int to,next; }p[maxm<<1]; void add_edge(int a,int b){ p[cnt].to=b; p[cnt].next=head[a]; head[a]=cnt++; return ; } void dfs(int x,int f){ dep[x]=dep[f]+1; fa[x][0]=f; for(int i=1;i<=N;++i){ fa[x][i]=fa[fa[x][i-1]][i-1]; } for(int i=head[x];i;i=p[i].next){ // debug(p[i].to); if(p[i].to!=f) dfs(p[i].to,x); } return ; } int lca(int u,int v){ if(dep[u]<dep[v]) swap(u,v); for(int i=N;i>=0;--i){ if(dep[fa[u][i]]>=dep[v]) u=fa[u][i]; } if(u==v) return v; for(int i=N;i>=0;--i){ if(fa[u][i]!=fa[v][i]){ u=fa[u][i]; v=fa[v][i]; } } return fa[v][0]; } int swimming(int x,int len){ int p=1; while(len){ if(len&1) x=fa[x][p-1]; len>>=1; ++p; } return x; } void solve(){ cin>>n; int a,b,c; for(int i=1;i<n;++i){ cin>>a>>b; add_edge(a,b); add_edge(b,a); } dfs(1,0); cin>>q; while(q--){ cin>>a>>b>>c; if(a==b){ cout<<a<<'\n'; continue; } int m=lca(a,b),len=dep[a]+dep[b]-dep[m]*2; if(len<=c) cout<<b<<'\n'; else{ len-=c; if(c>=dep[a]-dep[m]){ cout<<swimming(b,len)<<'\n'; }else{ cout<<swimming(a,c)<<'\n'; } } } return ; } signed main(){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); int _=1; // cin>>_; while(_--){ solve(); } return 0; }
F 题
cf 609 E. Minimum spanning tree for each edge
题意:
给你一个无自环和重边的图,对于图中的每一条边,求其最小生成树
思路:
朴素思想,每一条边均求一次最小生成树,应该会超时
我们先考虑原图存在的最小生成树。如果说问到某条包含在最小生成树中的边时,直接输出该图的最小生成树的边权之和即可;但问到不在最小生成树中的边时,我们将这条边加入所得的最小生成树就会得到一个环,我们去掉当前环上的权值最大的一条边(非指定边),所得即可包含新的指定边的最小生成树(证明?)
先求出最小生成树:对于在最小生成树中的边,答案就是最小生成树的边权和;对于不在最小生成树中的边(u, v),如果我们把这条边加入到最小生成树中,我们会得到一个环,我们如果去掉在原最小生成树上 u 到 v 的路径上的边权最大的边即可得到包含边 (u, v) 的最小生成树。
那么问题来了,怎么找那条权值最大的边?新学的lca给你这个帮助。应该很容易发现,求新加一边两端点的lca时所遍历的点就是在最小生成树上形成环的点。那么我们就可以利用lca缩短我们寻找最大值所需要的时间。这里使用st表辅助,因为倍增和st表的构造 什么“一拍即合!”
整体过程就是:先kruskal或prim求最小生成树,再dfs构造树上lca和st表,再对于每一条边利用lca求得去掉的最大值,最后输出答案即可
下为代码:
这份代码写的有些赘余了,有空再优化一下[ ]
//>>>Qiansui #include<map> #include<set> #include<list> #include<stack> #include<cmath> #include<queue> #include<deque> #include<cstdio> #include<string> #include<vector> #include<utility> #include<iomanip> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #include<functional> #define ll long long #define ull unsigned long long #define mem(x,y) memset(x,y,sizeof(x)) #define debug(x) cout << #x << " = " << x << endl #define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl //#define int long long inline ll read() { ll x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();} return x*f; } using namespace std; typedef pair<int,int> pii; typedef pair<ll,ll> pll; typedef pair<ull,ull> pull; typedef pair<double,double> pdd; /* 最小生成树+LCA 吐血,错了半天竟然是因为一个字母~ */ const int maxm=2e5+5,inf=0x3f3f3f3f,mod=998244353,N=20; int n,m,cnt=1,fa[maxm]; ll max_ele[maxm][N+1],w; int f[maxm][N+1],dep[maxm]; bool vis[maxm]; struct node{ ll v,w; node(ll a,ll b){ v=a;w=b; } }; vector<node> e[maxm]; struct edge{ ll u,v,w; bool operator < (const edge & a)const { return a.w<w; } }p[maxm]; priority_queue<edge> q; void pre(){//预处理并查集 for(int i=0;i<=n;++i) fa[i]=i; return ; } int findfa(int x) { return fa[x]==x?x:fa[x]=findfa(fa[x]); } void kruskal(){//求最小生成树 w=0; int u,v,a,b; while(!q.empty()){ edge t=q.top(); q.pop(); a=findfa(t.u); b=findfa(t.v); if(a==b) continue; else{ fa[b]=a; w+=t.w; e[t.u].push_back(node(t.v,t.w)); e[t.v].push_back(node(t.u,t.w)); } } return ; } void dfs(int x,int fath,ll val){//dfs求倍增lca,同时维护一个最大值st表 dep[x]=dep[fath]+1; f[x][0]=fath; vis[x]=true; max_ele[x][0]=val; for(int i=1;i<=N;++i){ f[x][i]=f[f[x][i-1]][i-1]; max_ele[x][i]=max(max_ele[x][i-1],max_ele[f[x][i-1]][i-1]); } for(int i=0;i<e[x].size();++i){ if(!vis[e[x][i].v]) dfs(e[x][i].v,x,e[x][i].w); } return ; } ll lca(int u,int v){//利用lca和st表找最大值,倍增刚好符合st表的条件 ll res=0; if(dep[u]<dep[v]) swap(u,v); for(int i=N;i>=0;--i){ if(dep[f[u][i]]>=dep[v]){ res=max(res,max_ele[u][i]); u=f[u][i]; } } if(u==v) return res; for(int i=N;i>=0;--i){ if(f[u][i]!=f[v][i]){ res=max(res,max_ele[u][i]); res=max(res,max_ele[v][i]); u=f[u][i]; v=f[v][i]; } } res=max(res,max_ele[u][0]); res=max(res,max_ele[v][0]); return res; } void solve(){ cin>>n>>m; pre(); for(int i=0;i<m;++i){ cin>>p[i].u>>p[i].v>>p[i].w; q.push(p[i]); } kruskal(); dfs(1,0,0); for(int i=0;i<m;++i){ ll t=lca(p[i].u,p[i].v); cout<<w-t+p[i].w<<'\n'; } return ; } signed main(){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); int _=1; // cin>>_; while(_--){ solve(); } return 0; }
本文来自博客园,作者:Qiansui,转载请注明原文链接:https://www.cnblogs.com/Qiansui/p/17536669.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!