使用矩阵乘法+线段树解决区间历史和问题的一种通用解法

news2025/6/8 13:17:31

文章目录

  • 前言
  • P8868 [NOIP2022] 比赛
  • CF1824D
  • P9990/2020 ICPC EcFinal G

前言

一般解决普通的区间历史和,只需要定义辅助 c = h s − t ⋅ a c=hs-t\cdot a c=hsta 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 maxaimaxbj,也就是求 ∑ 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 LlrRlirmaxailirmaxbi

考虑对所有询问离线,从左到右扫一遍,维护以 i i i为右端点的答案,询问就是查询区间 max ⁡ a ⋅ max ⁡ b \max a \cdot \max b maxamaxb的历史和

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+klenbab+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=lrj=1yg(i,j)i=lx1g(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的数据测试
在这里插入图片描述
从上到下分别是改良后的矩阵乘法(即本文章使用的),多重标记,矩阵乘法未减少状态,以及两个普通矩阵乘法

可以看到优化还是很明显的,比赛的时候时间充足找不到打多重标记的做法可以使用,优点就是非常套路,缺点就是代码量会大一点,并且非常难调(几个初始矩阵一定要用对)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2404172.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何从浏览器中导出网站证书

以导出 GitHub 证书为例&#xff0c;点击 小锁 点击 导出 注意&#xff1a;这里需要根据你想要证书格式手动加上后缀名&#xff0c;我的是加 .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 应用服务器实现…

ideal2022.3.1版本编译项目报java: OutOfMemoryError: insufficient memory

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

centos7编译安装LNMP架构

一、LNMP概念 LNMP架构是一种常见的网站服务器架构&#xff0c;由Linux操作系统、Nginx Web服务器、MySQL数据库和PHP后端脚本语言组成。 1 用户请求&#xff1a;用户通过浏览器输入网址&#xff0c;请求发送到Nginx Web服务器。 2 Nginx处理&#xff1a;Nginx接收请求后&…

Spring Boot 3.3 + MyBatis 基础教程:从入门到实践

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

征文投稿:如何写一份实用的技术文档?——以软件配置为例

&#x1f4dd; 征文投稿&#xff1a;如何写一份实用的技术文档&#xff1f;——以软件配置为例 目录 [TOC](目录)&#x1f9ed; 技术文档是通往成功的“说明书”&#x1f4a1; 一、明确目标读者&#xff1a;他们需要什么&#xff1f;&#x1f4cb; 二、结构清晰&#xff1a;让读…

tensorflow image_dataset_from_directory 训练数据集构建

以数据集 https://www.kaggle.com/datasets/vipoooool/new-plant-diseases-dataset 为例 目录结构 训练图像数据集要求&#xff1a; 主目录下包含多个子目录&#xff0c;每个子目录代表一个类别。每个子目录中存储属于该类别的图像文件。 例如 main_directory/ ...cat/ ...…

GOOUUU ESP32-S3-CAM 果云科技开发板开发指南(一)(超详细!)Vscode+espidf 通过摄像头拍摄照片并存取到SD卡中,文末附源码

看到最近好玩的开源项目比较多&#xff0c;就想要学习一下esp32的开发&#xff0c;目前使用比较多的ide基本上是arduino、esp-idf和platformio&#xff0c;前者编译比较慢&#xff0c;后两者看到开源大佬的项目做的比较多&#xff0c;所以主要学习后两者。 本次使用的硬件是GO…

全流程开源!高德3D贴图生成系统,白模一键生成真实感纹理贴图

导读 MVPainter 随着3D生成从几何建模迈向真实感还原&#xff0c;贴图质量正逐渐成为决定3D资产视觉表现的核心因素。我们团队自研的MVPainter系统&#xff0c;作为业内首个全流程开源的3D贴图生成方案&#xff0c;仅需一张参考图与任意白模&#xff0c;即可自动生成对齐精确…

html 滚动条滚动过快会留下边框线

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

数据通信与计算机网络——数据与信号

主要内容 模拟与数字 周期模拟信号 数字信号 传输减损 数据速率限制 性能 注&#xff1a;数据必须被转换成电磁信号才能进行传输。 一、模拟与数字 数据以及表示数据的信号可以使用模拟或者数字的形式。数据可以是模拟的也可以是数字的&#xff0c;模拟数据是连续的采用…

【LLM大模型技术专题】「入门到精通系列教程」LangChain4j与Spring Boot集成开发实战指南

LangChain4j和SpringBoot入门指南 LangChain4jLangchain4j API语言模型消息类型内存对象ChatMemory接口的主要实现设置 API 密钥SpringBoot Configuration配置ChatLanguageModelStreamingChatLanguageModel初始化ChatModel对象模型配置分析介绍说明通过JavaConfig创建ChatModel…

Vue3 GSAP动画库绑定滚动条视差效果 绑定滚动条 滚动条动画 时间轴

介绍 GSAP 用于创建高性能、可控制的动画效果。由 GreenSock 团队开发&#xff0c;旨在提供流畅、快速、稳定的动画效果&#xff0c;并且兼容各种浏览器。 提供了多个插件&#xff0c;扩展了动画的功能&#xff0c;如 ScrollTrigger&#xff08;滚动触发动画&#xff09;、Dra…

grafana-mcp-analyzer:基于 MCP 的轻量 AI 分析监控图表的运维神器!

还在深夜盯着 Grafana 图表手动排查问题&#xff1f;今天推荐一个让 AI 能“读图说话”的开源神器 —— grafana-mcp-analyzer。 想象一下这样的场景&#xff1a; 凌晨3点&#xff0c;服务器告警响起。。。你睁着惺忪的眼睛盯着复杂的监控图表 &#x1f635;‍&#x1f4ab;花…

【题解-洛谷】B3622 枚举子集(递归实现指数型枚举)

题目&#xff1a;B3622 枚举子集&#xff08;递归实现指数型枚举&#xff09; 题目描述 今有 n n n 位同学&#xff0c;可以从中选出任意名同学参加合唱。 请输出所有可能的选择方案。 输入格式 仅一行&#xff0c;一个正整数 n n n。 输出格式 若干行&#xff0c;每行…

(LeetCode 每日一题)3170. 删除星号以后字典序最小的字符串(贪心+栈)

题目&#xff1a;3170. 删除星号以后字典序最小的字符串 思路&#xff1a;贪心栈&#xff0c;时间复杂度0(n)。 对于每一个‘ * ’&#xff0c;优先选最右边的最小字符&#xff0c;才会使最终得到的字符串最小。 用栈&#xff0c;来记录每个字符的位置下标。细节看注释。 C版本…

使用 HTML + JavaScript 实现文章逐句高亮朗读功能

在这个信息爆炸的时代&#xff0c;我们每天都要面对大量的文字阅读。无论是学习、工作还是个人成长&#xff0c;阅读都扮演着至关重要的角色。然而&#xff0c;在快节奏的生活中&#xff0c;我们往往难以找到足够的安静时间专注于阅读。本文用 HTML JavaScript 实现了一个基于…

双碳时代,能源调度的难题正从“发电侧”转向“企业侧”

安科瑞刘鸿鹏 摘要 在“双碳”战略和能源结构转型的大背景下&#xff0c;企业储能电站逐步成为提升能源利用效率、增强用能韧性的重要手段。随着系统规模扩大与运行复杂度提升&#xff0c;如何对光伏、储能、负荷等流进行实时调控&#xff0c;成为智慧用能的关键。ACCU100微…

3. 简述node.js特性与底层原理

&#x1f63a;&#x1f63a;&#x1f63a; 一、Node.js 底层原理&#xff08;简化版&#xff09; Node.js 是一个 基于 Chrome V8 引擎构建的 JavaScript 运行时&#xff0c;底层核心由几部分组成&#xff1a; 组成部分简要说明 1.V8 引擎 将 JS 编译成机器码执行&#xff0…

OpenCV CUDA模块图像处理------创建一个模板匹配(Template Matching)对象函数createTemplateMatching()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 创建一个用于在 GPU 上执行模板匹配的 TemplateMatching 对象。 该函数返回一个指向 TemplateMatching 的智能指针&#xff08;Ptr&#xff09;…