使用矩阵乘法+线段树解决区间历史和问题的一种通用解法
文章目录
- 前言
- P8868 [NOIP2022] 比赛
- CF1824D
- P9990/2020 ICPC EcFinal G
前言
一般解决普通的区间历史和,只需要定义辅助 c = h s − t ⋅ a c=hs-t\cdot a c=hs−t⋅a, h s hs hs是历史和, a a a是区间和, t t t是时间戳,维护 a , c a,c a,c数组的区间加即可
但是如果题目更复杂一点,就要设置不同的tag,考虑下放顺序和影响,非常费脑子
一种无脑的方法就是构造矩阵,如果单纯使用普通的矩阵乘法,那么总复杂度会多 C 3 C^3 C3,其中 C C C为向量长度
本文的目的就是优化矩阵乘法的过程,实际上就是对矩阵乘法循环展开,只不过个人认为能更优雅一点罢了
P8868 [NOIP2022] 比赛
题意给出 a , b a,b a,b两个数组,多次询问, [ L , R ] [L,R] [L,R]的所有子区间的 max a i ⋅ max b j \max a_i \cdot \max b_j maxai⋅maxbj,也就是求 ∑ L ≤ l ≤ r ≤ R max l ≤ i ≤ r a i ⋅ max l ≤ i ≤ r b i \sum_{L \leq l \leq r \leq R }\max_{l \leq i \leq r}a_i \cdot \max_{l \leq i \leq r} b_i L≤l≤r≤R∑l≤i≤rmaxai⋅l≤i≤rmaxbi
考虑对所有询问离线,从左到右扫一遍,维护以 i i i为右端点的答案,询问就是查询区间 max a ⋅ max b \max a \cdot \max b maxa⋅maxb的历史和
对 a , b a,b a,b分别维护两个单调栈即可用线段树更新
线段树每个节点维护 [ a , b , a b , c , l e n ] [a,b,ab,c,len] [a,b,ab,c,len]表示区间 a i a_i ai的和, b i b_i bi的和, a i b i a_ib_i aibi的和, a i b i a_ib_i aibi的历史和,区间长度
那么对于区间 a a a加 k k k,有
( a b a b c l e n ) T ⋅ ( 1 0 0 0 0 0 1 k 0 0 0 0 1 0 0 0 0 0 1 0 k 0 0 0 1 ) = ( a + k ⋅ l e n b a b + k b c l e n ) T \begin{pmatrix} a\\b\\ab\\c\\len \end{pmatrix}^T\cdot \begin{pmatrix} 1&0&0&0&0\\ 0&1&k&0&0\\ 0&0&1&0&0\\ 0&0&0&1&0\\ k&0&0&0&1 \end{pmatrix}= \begin{pmatrix} a+k \cdot len\\b\\ab+kb\\c\\len \end{pmatrix}^T ababclen T⋅ 1000k010000k1000001000001 = a+k⋅lenbab+kbclen T
同理,区间 b b b加 k k k,有
( 1 0 k 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 k 0 0 1 ) \begin{pmatrix} 1&0&k&0&0\\ 0&1&0&0&0\\ 0&0&1&0&0\\ 0&0&0&0&1\\ 0&k&0&0&1 \end{pmatrix} 100000100kk01000000000011
更新区间历史和
( 1 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 1 ) \begin{pmatrix} 1&0&0&0&0\\ 0&1&0&0&0\\ 0&0&1&1&0\\ 0&0&0&1&0\\ 0&0&0&0&1 \end{pmatrix} 1000001000001000011000001
虽然矩阵乘法是 5 3 5^3 53,但是可以发现很多状态是一直为 0 0 0,也就是没有用的,要想找到这些状态,我们只需要将所有矩阵初始不为0的状态设为1,跑一遍传递闭包,就可以知道所有状态,这里25个状态一共只有14个状态合法(实际上可以9个,主对角线恒为1)
然后我们可以打表来代替手写,打表代码如下
void solve(){vector<vector<int>> f={{1,0,1,0,0},{0,1,1,0,0},{0,0,1,1,0},{0,0,0,1,0},{1,1,0,0,1}};const int n=f.size();for(int k=0;k<n;k++){for(int i=0;i<n;i++){for(int j=0;j<n;j++)f[i][j]|=f[i][k]&f[k][j];}}string s="int ";for(int i=0;i<n;i++){for(int j=0;j<n;j++){if(f[i][j])s+="x"+to_string(i)+to_string(j)+",";}}s.pop_back();s+=";\n";s+="\n\nMatrix * Matrix:\n";for(int i=0;i<n;i++){for(int j=0;j<n;j++){if(!f[i][j])continue;s+="res.x"+to_string(i)+to_string(j)+"=";for(int k=0;k<n;k++){if(f[i][k]&f[k][j])s+="a.x"+to_string(i)+to_string(k)+"*b.x"+to_string(k)+to_string(j)+"+";}s.pop_back();s+=";\n";}}s+="\n\nVec * Matrix:\n";for(int i=0;i<n;i++){s+="res.x0"+to_string(i)+"=";for(int j=0;j<n;j++){if(f[j][i]){s+="a.x0"+to_string(j)+"*b.x"+to_string(j)+to_string(i)+"+";}}s.pop_back();s+=";\n";}s.pop_back();cout<<s;
}
/*
打印结果:
int x00,x02,x03,x11,x12,x13,x22,x23,x33,x40,x41,x42,x43,x44;Matrix * Matrix:
res.x00=a.x00*b.x00;
res.x02=a.x00*b.x02+a.x02*b.x22;
res.x03=a.x00*b.x03+a.x02*b.x23+a.x03*b.x33;
res.x11=a.x11*b.x11;
res.x12=a.x11*b.x12+a.x12*b.x22;
res.x13=a.x11*b.x13+a.x12*b.x23+a.x13*b.x33;
res.x22=a.x22*b.x22;
res.x23=a.x22*b.x23+a.x23*b.x33;
res.x33=a.x33*b.x33;
res.x40=a.x40*b.x00+a.x44*b.x40;
res.x41=a.x41*b.x11+a.x44*b.x41;
res.x42=a.x40*b.x02+a.x41*b.x12+a.x42*b.x22+a.x44*b.x42;
res.x43=a.x40*b.x03+a.x41*b.x13+a.x42*b.x23+a.x43*b.x33+a.x44*b.x43;
res.x44=a.x44*b.x44;Vec * Matrix:
res.x00=a.x00*b.x00+a.x04*b.x40;
res.x01=a.x01*b.x11+a.x04*b.x41;
res.x02=a.x00*b.x02+a.x01*b.x12+a.x02*b.x22+a.x04*b.x42;
res.x03=a.x00*b.x03+a.x01*b.x13+a.x02*b.x23+a.x03*b.x33+a.x04*b.x43;
res.x04=a.x04*b.x44;
*/
然后我们就可以利用打印结果,快速套上线段树板子
struct Matrix{int x00,x02,x03,x11,x12,x13,x22,x23,x33,x40,x41,x42,x43,x44;
};
struct Vec{int x00,x01,x02,x03,x04;
};
Matrix operator*(const Matrix &a,const Matrix &b){Matrix res;res.x00=a.x00*b.x00;res.x02=a.x00*b.x02+a.x02*b.x22;res.x03=a.x00*b.x03+a.x02*b.x23+a.x03*b.x33;res.x11=a.x11*b.x11;res.x12=a.x11*b.x12+a.x12*b.x22;res.x13=a.x11*b.x13+a.x12*b.x23+a.x13*b.x33;res.x22=a.x22*b.x22;res.x23=a.x22*b.x23+a.x23*b.x33;res.x33=a.x33*b.x33;res.x40=a.x40*b.x00+a.x44*b.x40;res.x41=a.x41*b.x11+a.x44*b.x41;res.x42=a.x40*b.x02+a.x41*b.x12+a.x42*b.x22+a.x44*b.x42;res.x43=a.x40*b.x03+a.x41*b.x13+a.x42*b.x23+a.x43*b.x33+a.x44*b.x43;res.x44=a.x44*b.x44;return res;
}
Vec operator * (const Vec &a,const Matrix &b){Vec res;res.x00=a.x00*b.x00+a.x04*b.x40;res.x01=a.x01*b.x11+a.x04*b.x41;res.x02=a.x00*b.x02+a.x01*b.x12+a.x02*b.x22+a.x04*b.x42;res.x03=a.x00*b.x03+a.x01*b.x13+a.x02*b.x23+a.x03*b.x33+a.x04*b.x43;res.x04=a.x04*b.x44;return res;
}
Vec operator +(const Vec &a,const Vec &b){return {a.x00+b.x00,a.x01+b.x01,a.x02+b.x02,a.x03+b.x03,a.x04+b.x04};
}
Matrix I={1,0,0,1,0,0,1,0,1,0,0,0,0,1};
Matrix C={1,0,0,1,0,0,1,1,1,0,0,0,0,1};
Matrix getA(int k){return Matrix{1,0,0,1,k,0,1,0,1,k,0,0,0,1};
}
Matrix getB(int k){return Matrix{1,k,0,1,0,0,1,0,1,0,k,0,0,1};
}
vector<int> a,b;
struct SegmentTree{struct Node{Vec v;Matrix tag=I;bool lazy=0;};vector<Node> t;void init(int n){t=vector<Node>(n<<2);build(1,1,n);}void pushup(int p){t[p].v=t[p<<1].v+t[p<<1|1].v;}void pushtag(int p,const Matrix &m){t[p].v=t[p].v*m;t[p].tag=t[p].tag*m;t[p].lazy=1;}void pushdown(int p){if(t[p].lazy){pushtag(p<<1,t[p].tag);pushtag(p<<1|1,t[p].tag);t[p].lazy=0;t[p].tag=I;}}void build(int p,int l,int r){if(l==r){t[p].v={a[l],b[l],a[l]*b[l],0,1};return;}int mid=l+r>>1;build(p<<1,l,mid);build(p<<1|1,mid+1,r);pushup(p);}void modify(int p,int l,int r,int ql,int qr,const Matrix &x){if(ql<=l&&r<=qr){pushtag(p,x);return;}pushdown(p);int mid=l+r>>1;if(ql<=mid)modify(p<<1,l,mid,ql,qr,x);if(qr>mid)modify(p<<1|1,mid+1,r,ql,qr,x);pushup(p);}int query(int p,int l,int r,int ql,int qr){if(ql<=l&&r<=qr)return t[p].v.x03;pushdown(p);int res=0,mid=l+r>>1;if(ql<=mid)res+=query(p<<1,l,mid,ql,qr);if(qr>mid)res+=query(p<<1|1,mid+1,r,ql,qr);return res;}
} t;
void solve(){int n,m;cin>>m>>n;vector<int> sta(n+1),stb(n+1);a.assign(n+1,0);b.assign(n+1,0);for(int i=1;i<=n;i++)cin>>a[i];for(int i=1;i<=n;i++)cin>>b[i];t.init(n);cin>>m;vector<PII> Que[n+1];for(int i=0;i<m;i++){int l,r;cin>>l>>r;Que[r].emplace_back(l,i);}vector<int> ans(m);int at=0,bt=0;for(int i=1;i<=n;i++){while(at&&a[sta[at]]<a[i]){t.modify(1,1,n,sta[at-1]+1,sta[at],getA(a[i]-a[sta[at]]));--at;}sta[++at]=i;while(bt&&b[stb[bt]]<b[i]){t.modify(1,1,n,stb[bt-1]+1,stb[bt],getB(b[i]-b[stb[bt]]));--bt;}stb[++bt]=i;t.modify(1,1,n,1,i,C);for(auto [l,id]:Que[i]){ans[id]=t.query(1,1,n,l,i);}}for(int i=0;i<m;i++)cout<<ans[i]<<"\n";
}
这样就可以通过此题了,复杂度大概能快个10倍
CF1824D
首先一样的套路,离线从左到右扫,差分一下,即求 ∑ i = l r ∑ j = 1 y g ( i , j ) − ∑ i = l x − 1 g ( i , j ) \sum_{i=l}^r\sum_{j=1}^yg(i,j)-\sum_{i=l}^{x-1}g(i,j) ∑i=lr∑j=1yg(i,j)−∑i=lx−1g(i,j)
用一个set维护最后一个数出现的位置,那么 g ( i , j ) 就是 s e t 里面第一个大于等于 g(i,j)就是set里面第一个大于等于 g(i,j)就是set里面第一个大于等于i 的位置; 的位置; 的位置;对于一个 g ( i , j ) g(i,j) g(i,j),考虑哪些 i i i的贡献发生改变,手玩一下发现是一段区间覆盖,可以用区间加代替
那么区间加,区间历史和,就是套版子了
维护向量 [ s u m , h i s , l e n ] [sum,his,len] [sum,his,len]表示和,历史和,长度
区间加矩阵:
( 1 0 0 0 1 0 k 0 1 ) \begin{pmatrix} 1&0&0\\ 0&1&0\\ k&0&1 \end{pmatrix} 10k010001
更新矩阵
( 1 1 0 0 1 0 1 0 1 ) \begin{pmatrix} 1&1&0\\ 0&1&0\\ 1&0&1 \end{pmatrix} 101110001
打表简化常数,最终发现只需要维护6个变量
按照之前的步骤写,不需要手动卡常也能过
struct Matrix{int x00,x01,x11,x20,x21,x22;
};
struct Vec{int x00,x01,x02;
};
Matrix operator*(const Matrix &a,const Matrix &b){Matrix res;res.x00=a.x00*b.x00;res.x01=a.x00*b.x01+a.x01*b.x11;res.x11=a.x11*b.x11;res.x20=a.x20*b.x00+a.x22*b.x20;res.x21=a.x20*b.x01+a.x21*b.x11+a.x22*b.x21;res.x22=a.x22*b.x22;return res;
}
Vec operator * (const Vec &a,const Matrix &b){Vec res;res.x00=a.x00*b.x00+a.x02*b.x20;res.x01=a.x00*b.x01+a.x01*b.x11+a.x02*b.x21;res.x02=a.x02*b.x22;return res;
}
Vec operator +(const Vec &a,const Vec &b){return {a.x00+b.x00,a.x01+b.x01,a.x02+b.x02,};
}
Matrix I={1,0,1,0,0,1};
Matrix C={1,1,1,0,0,1};
Matrix getA(int k){return Matrix{1,0,1,k,0,1};
}
vector<int> a,b;
struct SegmentTree{struct Node{Vec v;Matrix tag=I;bool lazy=0;};vector<Node> t;void init(int n){t=vector<Node>(n<<2);build(1,1,n);}void pushup(int p){t[p].v=t[p<<1].v+t[p<<1|1].v;}void pushtag(int p,const Matrix &m){t[p].v=t[p].v*m;t[p].tag=t[p].tag*m;t[p].lazy=1;}void pushdown(int p){if(t[p].lazy){pushtag(p<<1,t[p].tag);pushtag(p<<1|1,t[p].tag);t[p].lazy=0;t[p].tag=I;}}void build(int p,int l,int r){if(l==r){t[p].v={0,0,1};return;}int mid=l+r>>1;build(p<<1,l,mid);build(p<<1|1,mid+1,r);pushup(p);}void modify(int p,int l,int r,int ql,int qr,const Matrix &x){if(ql<=l&&r<=qr){pushtag(p,x);return;}pushdown(p);int mid=l+r>>1;if(ql<=mid)modify(p<<1,l,mid,ql,qr,x);if(qr>mid)modify(p<<1|1,mid+1,r,ql,qr,x);pushup(p);}int query(int p,int l,int r,int ql,int qr){if(ql<=l&&r<=qr)return t[p].v.x01;pushdown(p);int res=0,mid=l+r>>1;if(ql<=mid)res+=query(p<<1,l,mid,ql,qr);if(qr>mid)res+=query(p<<1|1,mid+1,r,ql,qr);return res;}
} t;
void solve(){int n,q;cin>>n>>q;vector<int> a(n+1);for(int i=1;i<=n;i++)cin>>a[i];vector<int> ans(q);vector<array<int,4>> Que[n+1];for(int i=0;i<q;i++){int l,r,L,R;cin>>l>>r>>L>>R;Que[R].push_back({l,r,i,1});Que[L-1].push_back({l,r,i,-1});}t.init(n);set<int> s={0};vector<int> pre(n+1);for(int i=1;i<=n;i++){if(pre[a[i]]){auto j=s.lower_bound(pre[a[i]]);int l=*prev(j)+1,r=*j;int v=(next(j)==s.end())?i-*j:*next(j)-*j;// cout<<l<<" "<<r<<" "<<v<<"\n";t.modify(1,1,n,l,r,getA(v));s.erase(j);}t.modify(1,1,n,i,i,getA(i));pre[a[i]]=i;s.insert(i);t.modify(1,1,n,1,i,C);for(auto [l,r,id,op]:Que[i]){r=min(r,i);ans[id]+=op*t.query(1,1,n,l,r);}}for(int i=0;i<q;i++)cout<<ans[i]<<"\n";
}
P9990/2020 ICPC EcFinal G
#include<bits/stdc++.h>
#define N 1000005
#define rd read()
#define int long long
using namespace std;
int n,m,p[N],a[N],ans[N],t,l,r,op,las[N];
vector<pair<int,int> >q[N];
stack<int>s;
inline int read()
{int 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*10+ch-'0',ch=getchar();return x*f;
}
struct ver{int x11,x21,x31;ver operator + (const ver &o){return {x11+o.x11,x21+o.x21,x31+o.x31};}
}tr[N<<2];
struct mat{int x11,x12;int x21,x22;int x31,x32;mat operator * (const mat &o){mat res;res.x11=x11*o.x11+x12*o.x21;res.x12=x11*o.x12+x12*o.x22;res.x21=x21*o.x11+x22*o.x21;res.x22=x21*o.x12+x22*o.x22;res.x31=x31*o.x11+x32*o.x21+o.x31;res.x32=x31*o.x12+x32*o.x22+o.x32;return res;}ver operator * (const ver &o){return {x11*o.x11+x12*o.x21,x21*o.x11+x22*o.x21,x31*o.x11+x32*o.x21+o.x31};}
}tag[N<<2];
inline void pushup(int k){tr[k]=tr[k<<1]+tr[k<<1|1];}
inline void build(int k,int l,int r){tag[k]={1,0,0,1,0,0};if(l==r){return tr[k].x21=1,void();}int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k);
}
inline void add(int k,mat v){tag[k]=v*tag[k];tr[k]=v*tr[k];
}
inline void pushdown(int k){add(k<<1,tag[k]);add(k<<1|1,tag[k]);tag[k]={1,0,0,1,0,0};
}
inline void modify(int k,int l,int r,int x,int y,mat v){if(x<=l&&r<=y){return add(k,v),void();}int mid=l+r>>1;pushdown(k);if(x<=mid) modify(k<<1,l,mid,x,y,v);if(y>mid) modify(k<<1|1,mid+1,r,x,y,v);pushup(k);
}
inline ver query(int k,int l,int r,int x,int y){if(x<=l&&r<=y){return tr[k];}int mid=l+r>>1;ver res={0,0,0};pushdown(k);if(x<=mid) res=res+query(k<<1,l,mid,x,y);if(y>mid) res=res+query(k<<1|1,mid+1,r,x,y);return res;
}
signed main(){n=rd;build(1,1,n);for(int i=1;i<=n;i++) a[i]=rd;m=rd;for(int i=1;i<=m;i++) l=rd,r=rd,q[r].push_back({i,l});for(int i=1;i<=n;i++){modify(1,1,n,las[a[i]]+1,i,{0,1,1,0,0,0});modify(1,1,n,1,n,{1,0,0,1,1,0});las[a[i]]=i;for(auto [id,l]:q[i]) ans[id]=query(1,1,n,l,i).x31;}for(int i=1;i<=m;i++) cout<<ans[i]<<'\n';return 0;
}
拿CF ECFinal的数据测试
从上到下分别是改良后的矩阵乘法(即本文章使用的),多重标记,矩阵乘法未减少状态,以及两个普通矩阵乘法
可以看到优化还是很明显的,比赛的时候时间充足找不到打多重标记的做法可以使用,优点就是非常套路,缺点就是代码量会大一点,并且非常难调(几个初始矩阵一定要用对)
相关文章:

使用矩阵乘法+线段树解决区间历史和问题的一种通用解法
文章目录 前言P8868 [NOIP2022] 比赛CF1824DP9990/2020 ICPC EcFinal G 前言 一般解决普通的区间历史和,只需要定义辅助 c h s − t ⋅ a chs-t\cdot a chs−t⋅a, h s hs hs是历史和, a a a是区间和, t t t是时间戳,…...
React Navive初识
文章目录 搭建开发环境安装 Node、homebrew、Watchman安装 Node安装 homebrew安装 watchman 安装 React Native 的命令行工具(react-native-cli)创建新项目编译并运行 React Native 应用在 ios 模拟器上运行 调试访问 App 内的开发菜单 搭建开发环境 在…...
scss(sass)中 的使用说明
在 SCSS(Sass)中,& 符号是一个父选择器引用,它代表当前嵌套规则的外层选择器。主要用途如下: 1. 连接伪类/伪元素 scss 复制 下载 .button {background: blue;&:hover { // 相当于 .button:hoverbackgrou…...

如何从浏览器中导出网站证书
以导出 GitHub 证书为例,点击 小锁 点击 导出 注意:这里需要根据你想要证书格式手动加上后缀名,我的是加 .crt 双击文件打开...

低功耗MQTT物联网架构Java实现揭秘
文章目录 一、引言二、相关技术概述2.1 物联网概述2.2 MQTT协议java三、基于MQTT的Iot物联网架构设计3.1 架构总体设计3.2 MQTT代理服务器选择3.3 物联网设备设计3.4 应用服务器设计四、基于MQTT的Iot物联网架构的Java实现4.1 开发环境搭建4.2 MQTT客户端实现4.3 应用服务器实现…...
总结HTML中的文本标签
总结HTML中的文本标签 文章目录 总结HTML中的文本标签引言一、标题标签(h1 - h6)语法示例使用建议 二、段落标签(p)语法示例使用建议 三、文本节点标签(span)语法示例使用建议 四、粗体标签(b&a…...
python版若依框架开发:前端开发规范
python版若依框架开发 从0起步,扬帆起航。 python版若依部署代码生成指南,迅速落地CURD!项目结构解析前端开发规范文章目录 python版若依框架开发新增 view新增 api新增组件新增样式引⼊依赖新增 view 在 @/views文件下 创建对应的文件夹,一般性一个路由对应⼀个文件, 该…...
AI推理服务的高可用架构设计
AI推理服务的高可用架构设计 在传统业务系统中,高可用架构主要关注服务冗余、数据库容灾、限流熔断等通用能力。而在AI系统中,尤其是大模型推理服务场景下,高可用架构面临更加复杂的挑战,如推理延迟敏感性、GPU资源稀缺性、模型版本切换频繁等问题。本节将专门探讨如何构建…...
GPU集群故障分析:大型AI训练中的硬件问题与影响
GPU集群故障分析:大型AI训练中的硬件问题与影响 核心问题 在大型AI计算集群(如使用上千块GPU卡训练大模型)中: GPU硬件会出哪些毛病?这些问题发生的频率、严重程度如何?最终对AI训练任务有什么影响&#…...

ideal2022.3.1版本编译项目报java: OutOfMemoryError: insufficient memory
最近换了新电脑,用新电脑拉项目配置后,启动时报错,错误描述 idea 启动Springboot项目在编译阶段报错:java: OutOfMemoryError: insufficient memory 2. 处理方案 修改VM参数,分配更多内存 ❌ 刚刚开始以为时JVM内存设置…...

centos7编译安装LNMP架构
一、LNMP概念 LNMP架构是一种常见的网站服务器架构,由Linux操作系统、Nginx Web服务器、MySQL数据库和PHP后端脚本语言组成。 1 用户请求:用户通过浏览器输入网址,请求发送到Nginx Web服务器。 2 Nginx处理:Nginx接收请求后&…...
接口限频算法:漏桶算法、令牌桶算法、滑动窗口算法
文章目录 限频三大算法对比与选型建议一、漏桶算法(Leaky Bucket Algorithm)1.核心原理2.实现3.为什么要限制漏桶容量4.优缺点分析 二、令牌桶算法(Token Bucket Algorithm)1.核心原理2.实现(1)单机实现&am…...

Spring Boot 3.3 + MyBatis 基础教程:从入门到实践
Spring Boot 3.3 MyBatis 基础教程:从入门到实践 在当今的Java开发领域,Spring Boot和MyBatis是构建高效、可维护的后端应用的两个强大工具。Spring Boot简化了Spring应用的初始搭建和开发过程,而MyBatis则提供了一种灵活的ORM(…...

征文投稿:如何写一份实用的技术文档?——以软件配置为例
📝 征文投稿:如何写一份实用的技术文档?——以软件配置为例 目录 [TOC](目录)🧭 技术文档是通往成功的“说明书”💡 一、明确目标读者:他们需要什么?📋 二、结构清晰:让读…...
【后端】RPC
不定期更新。 定义 RPC 是 Remote Procedure Call 的缩写,中文通常翻译为远程过程调用。作用 简化分布式系统开发。实现微服务架构,便于模块化、复用。提高系统性能和可伸缩性。提供高性能通信、负载均衡、容错重试机制。 在现代分布式系统、微服务架构…...
详细讲解Flutter GetX的使用
Flutter GetX 框架详解:状态管理、路由与依赖注入 GetX 是 Flutter 生态中一款强大且轻量级的全功能框架,集成了状态管理、路由管理和依赖注入三大核心功能。其设计理念是简洁高效,通过最小的代码实现最大的功能,特别适合快速开发…...
ReLU 新生:从死亡困境到强势回归
背景 在深度学习领域,激活函数的探索已成为独立研究课题。诸如 GELU、SELU 和 SiLU 等新型激活函数,因具备平滑梯度与出色的收敛特性,正备受关注。经典 ReLU 凭借简洁性、固有稀疏性及其独特优势拓扑特性,依旧受青睐。然而&#…...

tensorflow image_dataset_from_directory 训练数据集构建
以数据集 https://www.kaggle.com/datasets/vipoooool/new-plant-diseases-dataset 为例 目录结构 训练图像数据集要求: 主目录下包含多个子目录,每个子目录代表一个类别。每个子目录中存储属于该类别的图像文件。 例如 main_directory/ ...cat/ ...…...
QuickJS 如何发送一封邮件 ?
参阅:bellard.org : QuickJS 如何使用 qjs 执行 js 脚本 在 QuickJS 中发送邮件需要依赖外部库或调用系统命令,因为 QuickJS 本身不包含 SMTP 功能。以下是两种实现方法: 方法 1:调用系统命令(推荐) 使…...
clickhouse 和 influxdb 选型
以下是 ClickHouse、InfluxDB 和 HBase 在体系架构、存储引擎、数据类型、性能及场景的详细对比分析: 🏗️ 一、体系架构对比 维度ClickHouseInfluxDBHBase设计目标大规模OLAP分析,高吞吐复杂查询 时序数据采集与监控,优化时间线管理高吞吐随机…...

GOOUUU ESP32-S3-CAM 果云科技开发板开发指南(一)(超详细!)Vscode+espidf 通过摄像头拍摄照片并存取到SD卡中,文末附源码
看到最近好玩的开源项目比较多,就想要学习一下esp32的开发,目前使用比较多的ide基本上是arduino、esp-idf和platformio,前者编译比较慢,后两者看到开源大佬的项目做的比较多,所以主要学习后两者。 本次使用的硬件是GO…...
C++学习思路
C++知识体系详细大纲 一、基础语法 (一)数据类型 基本数据类型 整数类型(int, short, long, long long)浮点类型(float, double, long double)字符类型(char, wchar_t, char16_t, char32_t)布尔类型(bool)复合数据类型 数组结构体(struct)联合体(union)枚举类型…...

全流程开源!高德3D贴图生成系统,白模一键生成真实感纹理贴图
导读 MVPainter 随着3D生成从几何建模迈向真实感还原,贴图质量正逐渐成为决定3D资产视觉表现的核心因素。我们团队自研的MVPainter系统,作为业内首个全流程开源的3D贴图生成方案,仅需一张参考图与任意白模,即可自动生成对齐精确…...
使用Conda管理服务器多版本Python环境的完整指南
在服务器环境中管理多个Python版本是开发者和系统管理员常见的需求,尤其是当不同项目依赖特定版本的Python时。本文将重点介绍如何通过Conda实现多版本Python的隔离与管理,确保服务器环境的稳定性和灵活性。 为什么需要多版本Python管理? 服…...

html 滚动条滚动过快会留下边框线
滚动条滚动过快时,会留下边框线 但其实大部分时候是这样的,没有多出边框线的 滚动条滚动过快时留下边框线的问题通常与滚动条样式和滚动行为有关。这种问题可能出现在使用了自定义滚动条样式的情况下。 注意:使用方法 6 好使,其它…...

数据通信与计算机网络——数据与信号
主要内容 模拟与数字 周期模拟信号 数字信号 传输减损 数据速率限制 性能 注:数据必须被转换成电磁信号才能进行传输。 一、模拟与数字 数据以及表示数据的信号可以使用模拟或者数字的形式。数据可以是模拟的也可以是数字的,模拟数据是连续的采用…...

【LLM大模型技术专题】「入门到精通系列教程」LangChain4j与Spring Boot集成开发实战指南
LangChain4j和SpringBoot入门指南 LangChain4jLangchain4j API语言模型消息类型内存对象ChatMemory接口的主要实现设置 API 密钥SpringBoot Configuration配置ChatLanguageModelStreamingChatLanguageModel初始化ChatModel对象模型配置分析介绍说明通过JavaConfig创建ChatModel…...
Flask 基础与实战概述
一、Flask 基础知识 什么是 Flask? Flask 是一个基于 Python 的轻量级 Web 框架(微框架)。 特点:核心代码简洁,给予开发者更多选择空间。 与 Django 对比: Django 创建空项目生成多个文件,Flask 仅需一个文件即可实现简单应用(如 "Hello, World!")。 Flask …...
东芝Toshiba e-STUDIO2110AC打印机信息
基本信息 产品类型:数码复合机颜色类型:彩色涵盖功能:复印、打印、扫描接口类型:标配为 Ethernet(RJ45)10/100/1000BASE - T、USB2.0 高速;选配为 Wireless Lan、IEEE802.11b/g/n、blueteeth。中…...

Vue3 GSAP动画库绑定滚动条视差效果 绑定滚动条 滚动条动画 时间轴
介绍 GSAP 用于创建高性能、可控制的动画效果。由 GreenSock 团队开发,旨在提供流畅、快速、稳定的动画效果,并且兼容各种浏览器。 提供了多个插件,扩展了动画的功能,如 ScrollTrigger(滚动触发动画)、Dra…...