首页 > 其他分享 >线性基

线性基

时间:2024-10-10 11:04:43浏览次数:9  
标签:ck int LL long -- ans 线性

线性基

第 \(n+1\) 次学线性基,希望这次能学会。

模板

线性基通常用来处理异或相关的问题。

假如有 \(n\) 个数,我们想表示它们能相互异或得到的所有数组成的集合。

线性基就是针对这组数生成的集合,它们任意异或得到数的值域与原序列相同,并且满足是一个极小集合(数尽量少)。

联想基向量,这里每一个二进制位可以看成一维,线性基就是一组基向量。(好像叫极大线性无关组

插入一个数的过程:

假如我们想插入 \(x\),\(a_i\) 表示二进制位第二位的基底,如果已经存在基底,那么 \(x \gets x \oplus a_i\),否则 \(a_i \gets x\)。

因为 \(a \oplus b \oplus a = b\),所以不妨令 \(c = a \oplus b\),这样仍能得到 \(b\)。上面的过程其实就是这样。

对于这道题,求最大值,直接贪心从高位向低位取,但注意我们取了高位时不知道包不包含低位(第 \(i\) 位的基底只保证最高是 \(i\),不保证没有低位)。

所以注意判断只有异或后会使答案变大才要。

code
#include<bits/stdc++.h>
using namespace std;
const int N = 60;
#define LL long long
int n; LL ck[N];
inline void ins(LL x)
{
	for(int i=55;i>=0;i--)
		if((x>>i)&1)
		{
			if(!ck[i]) {ck[i]=x; break;}
			x^=ck[i];
		}
}

int main()
{
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		LL x; scanf("%lld",&x);
		ins(x);
	}
	LL ans=0;
	for(int i=55;i>=0;i--) if((ans^ck[i])>ans) ans^=ck[i];
	printf("%lld\n",ans);
	return 0;
}

彩灯

假如找到 \(cnt\) 个基底,那能异或得到的数的个数就是 \(2^{cnt}\)(每一个可选可不选,并且一定不会有重复的数)。

code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 60;
int n,m;
LL ck[N+5],ans;
void ins(LL x)
{
	for(int j=n-1;j>=0;j--)
		if((x>>j)&1)
		{
			if(!ck[j]) {ck[j]=x; ans++; break;}
			x^=ck[j];
		}
}
int main()
{
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		LL x=0;
		for(int j=n-1;j>=0;j--)
		{
			char c; scanf(" %c",&c);
			if(c=='O') x|=1ll<<j;
		}
		ins(x);
	}
	printf("%lld\n",(1ll<<ans)%2008);
	return 0;
}

元素

直接贪心,按价值从大到小排序,如果能插入线性基就算贡献,否则不算。

首先,由于线性基是一个 极大线性无关组,也就是 任意子集异或和不为零,所以能插进去说明异或和不为零。

贪心证明:

假设最大的数是 \(a_n\),但 \(a_n\) 不在最终答案中,最终答案为集合 \(A\)。

那必然有 \(a_n=\bigoplus_{a_j \in A} a_j\),交换任意一个 \(a_j\) 和 \(a_n\) 等式仍成立且更优。所以一定选 \(a_n\)

(问:拟阵是啥?)

code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 1e3+5;
int n,ans;
LL ck[62];
struct A {int v; LL num;} a[N];
void ins(LL x,int v)
{
	for(int i=60;i>=0;i--)
		if((x>>i)&1)
		{
			if(!ck[i]) {ans+=v; ck[i]=x; break;}
			x^=ck[i];
		}
}

int main()
{
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%lld%d",&a[i].num,&a[i].v);
	sort(a+1,a+1+n,[&] (A &x,A &y) {return x.v>y.v;});
	for(int i=1;i<=n;i++)
	{
		ins(a[i].num,a[i].v);
	}
	printf("%d\n",ans);
	return 0;
}

新Nim游戏

转化问题,先手想赢,那么先手拿完剩下的集合的任意子集异或和不为零。

发现先把较大的数插进去,组成一个极大线性无关组,剩下的拿走即可。

code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 105;
int n; LL a[N],ans,ck[N];

void ins(LL x,LL v)
{
	for(int i=30;i>=0;i--)
		if((x>>i)&1)
		{
			if(!ck[i]) {ck[i]=x; return;}
			x^=ck[i];
		}
	ans+=v;
}
int main()
{
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	sort(a+1,a+1+n,greater<int>());
	for(int i=1;i<=n;i++) ins(a[i],a[i]);
	printf("%lld\n",ans);
	return 0;
}

XOR

第 k 小板子。

发现原有的线性基不太好确定一个数在值域值域中的位置,我们试着把它转化为一个好看的形式。

为了有一个标准,我们令每个二进制位都只在它这位的基底上出现,其他的直接异或掉。

\[\begin{array}{cc} 1 &0 &1 &0 &0 \\ 0 &1 &0 &0 &0 \\ 0 &0 &1 &0 &1 \\ 0 &0 &0 &0 &1 \end{array} \Rightarrow \begin{array}{cc} 1 &0 &0 &0 &0 \\ 0 &1 &0 &0 &0 \\ 0 &0 &1 &0 &0 \\ 0 &0 &0 &0 &1 \end{array} \]

这样第 \(i\) 位基底恰好就是第 \(2_i\) 小的数,我们将 \(k\) 二进制拆分后发现它们的意义恰好完全一样。

code
#include<stdio.h>
#include<cstring>
using namespace std;
#define LL long long
const int N = 1e4+5;
int T,tot,n,q,m;
LL ck[65],tmp[65],cnt;
inline void ins(LL x)
{
	for(int i=60;i>=0;i--)
		if((x>>i)&1)
		{
			if(!ck[i]) {ck[i]=x; m++; return;}
			x^=ck[i];
		}
}
void init()
{
	cnt=0;
	for(int i=0;i<=60;i++)
	{
		if(!ck[i]) continue;
		for(int j=i-1;j>=0;j--)
			if((ck[i]>>j)&1) ck[i]^=ck[j];
		tmp[cnt++]=ck[i];
	}
}
inline LL que(LL k)
{
	k-=(n!=m); if(!k) return 0;
	if(k>=(1ll<<cnt)) return -1;
	LL res=0;
	for(int i=0;i<cnt;i++)
		if((k>>i)&1) res^=tmp[i];
	return res;
}
int main()
{
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	scanf("%d",&T);
	while(T--)
	{
		memset(ck,0,sizeof(ck)); m=0;
		printf("Case #%d:\n",++tot);
		scanf("%d",&n);
		for(int i=1;i<=n;i++) 
		{
			LL x; scanf("%lld",&x),ins(x);
		}
		init();
		scanf("%d",&q);
		while(q--)
		{
			LL x; scanf("%lld",&x);
			printf("%lld\n",que(x));
		}
	}
	return 0;
}

Operation

不是板。

容易想到扫描线处理出每一个右端点的答案。

因为线性基的时间空间都是 \(log\) 的,我们不妨直接开 \(a_{i,r}\) 表示第 \(i\) 位,右端点到 \(r\) 的线性基。

考虑如何确定左端点。

直接贪心(线性基和贪心最配?),对基底,我们只在乎这一位能否被表出,而不在乎基底的值,所以尽量选靠右的肯定最优。

单独开一个 \(pos_{i,r}\) 记录当前基底的位置,如果新加进来的更靠右,那么替换,继续判断被换下来的基能否往后插。

插入直接做就好了。

code
#include<stdio.h>
#include<cmath>
#include<algorithm>
using namespace std;
#define LL long long
const int N = 5e5+5;
int T,n,m;
int ck[N][32],pos[N][32];
void ins(int k,int x,int p)
{
	for(int i=30;i>=0;i--)
		if((x>>i)&1)
		{
			if(!ck[k][i]) {ck[k][i]=x; pos[k][i]=p; break;}
			else
			{
				if(pos[k][i]<p) swap(ck[k][i],x),swap(pos[k][i],p);
				x^=ck[k][i];
			}
		}
}
int main()
{
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
		{
			int x; scanf("%d",&x);
			for(int j=0;j<=30;j++) ck[i][j]=ck[i-1][j],pos[i][j]=pos[i-1][j];
			ins(i,x,i);
		}
		int ans=0;
		while(m--)
		{
			int c,l,r; scanf("%d%d",&c,&l);
			if(c==0)
			{
				scanf("%d",&r);
				l=(l^ans)%n+1; r=(r^ans)%n+1;
				if(l>r) swap(l,r);
				ans=0;
				for(int i=30;i>=0;i--) 
					if(pos[r][i]>=l&&(ans^ck[r][i])>ans) ans^=ck[r][i];
				printf("%d\n",ans);
			}
			else
			{
				l=l^ans; n++;
				for(int i=0;i<=30;i++) 	ck[n][i]=ck[n-1][i],pos[n][i]=pos[n-1][i];
				ins(n,l,n);
			}
		}
	}
	return 0;
}

Ivan and Burgers

同上

code
#include<stdio.h>
#include<cmath>
#include<algorithm>
using namespace std;
#define LL long long
const int N = 5e5+5;
int T,n,m;
int ck[N][21],pos[N][21];
void ins(int k,int x,int p)
{
	for(int i=20;i>=0;i--)
		if((x>>i)&1)
		{
			if(!ck[k][i]) {ck[k][i]=x; pos[k][i]=p; break;}
			else
			{
				if(pos[k][i]<p) swap(ck[k][i],x),swap(pos[k][i],p);
				x^=ck[k][i];
			}
		}
}
int main()
{
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int x; scanf("%d",&x);
		for(int j=0;j<=20;j++) ck[i][j]=ck[i-1][j],pos[i][j]=pos[i-1][j];
		ins(i,x,i);
	}
	scanf("%d",&m);
	while(m--)
	{
		int l,r,ans=0; scanf("%d%d",&l,&r);
		for(int i=20;i>=0;i--) 
			if(pos[r][i]>=l&&(ans^ck[r][i])>ans) ans^=ck[r][i];
		printf("%d\n",ans);
	}
 
	return 0;
}

最大XOR和路径

图论。

感觉难点不在线性基,重点理解异或的性质。

从简单的情况考虑,即只有一条链,肯定都选。

假如在这条链之外有环怎么办。(链不可能和环有重边,如果有,重边异或后不会有贡献,又变成链了。)

考虑走到环又走回来,中间的路径走了两边,异或后没了,所以选环就相当于选了一个单独的环。

所以我们找出 \(1 \to n\) 的链的异或和,再插入所有环的,找最大即可。

一条路径走两遍相当于没走!

图片来自:【[WC2011]最大XOR和路径】

code
#include<cstdio>
#include<math.h>
#include<algorithm>
#include<cstring>
using namespace std;
#define LL long long
const int N = 5e4+5,M = 1e5+5;
int n,m;
int head[N],tot; LL d[N];
struct E {int u,v; LL w;} e[M<<1];
inline void add(int u,int v,LL w) {e[++tot]={head[u],v,w}; head[u]=tot;}
bool vs[N];
LL ck[70];
inline void ins(LL x)
{
	for(int i=62;i>=0;i--)
		if((x>>i)&1)
		{
			if(!ck[i]) {ck[i]=x; break;}
			x^=ck[i];
		}
}
void dfs(int u,int f,LL res)
{
	d[u]=res; vs[u]=1;
	for(int i=head[u];i;i=e[i].u)
	{
		int v=e[i].v;
		if(!vs[v]) dfs(v,u,res^e[i].w);
		else ins(res^e[i].w^d[v]);
	}
}
int main()
{
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int x,y; LL z; scanf("%d%d%lld",&x,&y,&z);
		add(x,y,z); add(y,x,z);
	}
	dfs(1,0,0ll);
	LL ans=d[n];//主链的贡献
	for(int i=62;i>=0;i--) if((ans^ck[i])>ans) ans^=ck[i];
	printf("%lld\n",ans);
	return 0;
}

幸运数字

如果你知道线性基能在 \(log^2(t)\) 的复杂度内合并,就做完了。

先树剖转化为区间问题,发现就是若干个区间求异或最大值,区间内异或最大值同上。

最后将若干个区间的线性基合并,求最大值就行了。

复杂度 \(q\log(n)\log^2(t)\),vjudge 最优解。(你永远可以相信树剖)

code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N =2e4+5;
int n,q,pos[N][62];
LL a[N],ck[N][62];
int head[N],tot;
struct E {int u,v;} e[N<<1];
inline void add(int u,int v) {e[++tot]={head[u],v}; head[u]=tot;}
inline void ins(LL x,int k,int p)
{
	for(int i=61;i>=0;i--)
		if((x>>i)&1)
		{
			if(!ck[k][i]) {ck[k][i]=x; pos[k][i]=p; break;}
			else
			{
				if(pos[k][i]<p) swap(pos[k][i],p),swap(ck[k][i],x);
				x^=ck[k][i];
			}
		}
}
int sz[N],son[N],top[N],dfn[N],num,rk[N],fa[N],dep[N];
void dfs1(int u,int f)
{
	fa[u]=f; sz[u]=1; son[u]=-1; dep[u]=dep[f]+1;
	for(int i=head[u];i;i=e[i].u)
	{
		int v=e[i].v; if(v==f) continue;
		dfs1(v,u); sz[u]+=sz[v];
		if(son[u]==-1||sz[son[u]]<sz[v]) son[u]=v;
	}
}
void dfs2(int u,int t)
{
	top[u]=t; dfn[u]=++num; rk[num]=u;
	if(son[u]==-1) return;
	dfs2(son[u],t);
	for(int i=head[u];i;i=e[i].u)
	{
		int v=e[i].v; if(v==fa[u]||v==son[u]) continue;
		dfs2(v,v);
	}
}
LL tmp[62];
inline void merge(int l,int r)
{
	for(int i=61;i>=0;i--)
	{
		if(pos[r][i]>=l)
		{
			LL tt=ck[r][i];
			for(int j=i;j>=0;j--)
				if((tt>>j)&1)
				{
					if(!tmp[j]) {tmp[j]=tt; break;}
					tt^=tmp[j];
				}
		}
	}
}
inline LL que(int x,int y)
{
	LL res=0;
	memset(tmp,0,sizeof(tmp));
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		merge(dfn[top[x]],dfn[x]);
		x=fa[top[x]];
	}
	if(dfn[x]>dfn[y]) swap(x,y);
	merge(dfn[x],dfn[y]);
	for(int i=61;i>=0;i--) if((res^tmp[i])>res) res^=tmp[i];
	return res;
}
int main()
{
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	for(int i=1;i<n;i++)
	{
		int x,y; scanf("%d%d",&x,&y);
		add(x,y); add(y,x);
	}
	dfs1(1,0); dfs2(1,1);
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<=61;j++) ck[i][j]=ck[i-1][j],pos[i][j]=pos[i-1][j];
		ins(a[rk[i]],i,i);
	}
	while(q--)
	{
		int l,r; scanf("%d%d",&l,&r);
		printf("%lld\n",que(l,r));
	}
	return 0;
}

欧几里得的噩梦

题面

不需要线性基,但是题目本身就是在模拟线性基插入的过程。

性质很明显,每个二进制数只有两位是 \(1\),所以直接将这两位连边(只有一位那就和虚点连)。

显然如果成环了就不是最小的子集了,所以模拟生成树的过程加边就行。答案就是选了多少边。

code
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+5,mod = 1e9+7;
int n,m;
int fa[N],cnt;
inline long long qpow(long long a,int b)
{
	long long res=1;
	while(b)
	{
		if(b&1) res=res*a%mod;
		a=a*a%mod; b>>=1;
	}
	return res;
}
inline int find(int x) {return x==fa[x]?(x):(fa[x]=find(fa[x]));}
vector<int> v;
int main()
{
	freopen("Euclid.in","r",stdin);
	freopen("Euclid.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m+1;i++) fa[i]=i;
	for(int i=1;i<=n;i++)
	{
		int c,x,y=m+1; scanf("%d%d",&c,&x);
		if(c==2) scanf("%d",&y);
		int fx=find(x),fy=find(y);
		if(fx==fy) continue;
		fa[fx]=fy; cnt++;
		v.push_back(i);		
	}
	sort(v.begin(),v.end());
	printf("%lld %d\n",qpow(2,cnt),cnt);
	for(int i:v) printf("%d ",i);
	return 0;
}

装备购买

真·线性基

上面的题都是二进制下的线性基来处理异或问题,而真正的线性基其实是实数范围内的。

容易看出本题仍是求一个极大线性无关组。考虑如何将一个向量插入线性基。

类比二进制下的异或操作,我们插入的过程其实就是想消掉某一位。实数意义下的异或其实就是加减法。

假如对于向量 \(\bm{a}\)(\(\bm{a}_i\) 表示第 \(i\) 维的值),我们在第 \(i\) 维已经有了基底 \(\bm{c}\),那么想要消去 \(\bm{a}\) 的第 \(i\) 维,即 \(\bm{a} \gets \bm{a}-\frac{\bm{a}_i}{\bm{c}_i} \times \bm{c}\)。

其他操作和线性基一样。注意精度问题。

code
#include<bits/stdc++.h>
using namespace std;
#define LD long double
const int N = 505;
const LD del = 1e-7;
int n,m; long long ans,cnt;
struct A {int w; vector<LD> p;} a[N];
int ck[N];
inline void ins(int x)
{
	for(int j=m-1;j>=0;j--)
	{
		if(fabs(a[x].p[j])>del)
		{
			if(!ck[j]) {ck[j]=x; cnt++; ans+=a[x].w; break;}
			LD tmp=a[x].p[j]/a[ck[j]].p[j];
			for(int k=j;k>=0;k--) a[x].p[k]-=tmp*a[ck[j]].p[k];
		}
	}
}
int main()
{
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		a[i].p.resize(m);
		for(int j=0;j<m;j++) scanf("%Lf",&a[i].p[j]);
	}
	for(int i=1;i<=n;i++) scanf("%d",&a[i].w);
	sort(a+1,a+1+n,[&] (const A &x,const A &y) {return x.w<y.w;});
	for(int i=1;i<=n;i++) ins(i);
	printf("%d %lld\n",cnt,ans);
	return 0;
}

圣剑护符

好题捏。

容易想到树剖。发现如果路径上的数全能插进线性基,那么它一定是线性无关的,也就是不合法。

一开始想维护线性基大小,发现修改不太可做。

发现有一个很好的性质:如果多于 \(30\) 个数一定线性相关,鸽笼易证。

所以我们需要解决的就是小于三十个数是否线性相关,直接暴力插。

对于区间修改,差分!!!(总是想不到差分),用个树状数组维护一下就好了。(异或也可以用树状数组维护)

code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
int n,q;
int head[N],tot,a[N];
struct E{int u,v;} e[N<<1];
inline void add(int u,int v) {e[++tot]={head[u],v}; head[u]=tot;}
int sz[N],top[N],son[N],fa[N],dep[N],num,rk[N],dfn[N];
void dfs1(int u,int f)
{
	fa[u]=f; dep[u]=dep[f]+1; sz[u]=1; son[u]=-1;
	for(int i=head[u];i;i=e[i].u)
	{
		int v=e[i].v; if(v==f) continue;
		dfs1(v,u); sz[u]+=sz[v];
		if(son[u]==-1||sz[son[u]]<sz[v]) son[u]=v;
	}
}
void dfs2(int u,int t)
{
	top[u]=t; dfn[u]=++num; rk[num]=u;
	if(son[u]==-1) return;
	dfs2(son[u],t);
	for(int i=head[u];i;i=e[i].u)
	{
		int v=e[i].v; if(v==fa[u]||v==son[u]) continue;
		dfs2(v,v);
	}
}
inline int lca(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	if(dfn[x]>dfn[y]) swap(x,y);
	return x;
}
namespace BIT
{
	int c[N];
	inline void _mdf(int x,int v) {for(;x<=n;x+=(x&-x)) c[x]^=v;}
	inline int que(int x) {int res=0; for(;x;x-=(x&-x)) res^=c[x]; return res;}
	inline void mdf(int l,int r,int v) {_mdf(l,v); _mdf(r+1,v);}
} using namespace BIT;
int tmp[31];
bool ins(int l,int r)
{
	for(int i=l;i<=r;i++)
	{
		bool fl=0; int x=que(i);
		for(int j=30;j>=0;j--)
			if((x>>j)&1)
			{
				if(!tmp[j]) {tmp[j]=x; fl=1; break;}
				x^=tmp[j];
			}
		if(!fl) return 1;
	}
	return 0;
}
bool inspath(int x,int y)
{
	memset(tmp,0,sizeof(tmp));
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		if(ins(dfn[top[x]],dfn[x])) return 1;
		x=fa[top[x]];
	}
	if(dfn[x]>dfn[y]) swap(x,y);
	if(ins(dfn[x],dfn[y])) return 1;
	return 0;
}
void mdfpath(int x,int y,int z)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		mdf(dfn[top[x]],dfn[x],z);
		x=fa[top[x]];
	}
	if(dfn[x]>dfn[y]) swap(x,y);
	mdf(dfn[x],dfn[y],z);
}
int main()
{
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<n;i++)
	{
		int x,y; scanf("%d%d",&x,&y);
		add(x,y); add(y,x);
	}
	dfs1(1,0); dfs2(1,1);
	for(int i=1;i<=n;i++) _mdf(i,a[rk[i]]^a[rk[i-1]]);
	// for(int i=1;i<=n;i++) printf("%d ",que(i)); putchar('\n');
	while(q--)
	{
		string c; cin>>c;
		if(c[0]=='Q')
		{
			int x,y; scanf("%d%d",&x,&y);
			int l=lca(x,y),dis=dep[x]+dep[y]-(dep[l]<<1)+1;
			if(dis>30) printf("YES\n");
			else
			{
				if(inspath(x,y)) printf("YES\n");
				else printf("NO\n");
			}
		}
		else
		{
			int x,y,z; scanf("%d%d%d",&x,&y,&z);
			mdfpath(x,y,z);
		}
	}
	return 0;
}

标签:ck,int,LL,long,--,ans,线性
From: https://www.cnblogs.com/ppllxx-9G/p/18454696

相关文章

  • 背景颜色线性渐变、径向渐变、重复线性、镜像渐变
    线性渐变background-image:linear-gradient(方向,起始颜色,终止颜色);background-image:linear-gradient(toright,yellow,green);方向可以是:toleft、toright、totop、tobottom、角度30deg(指的是顺时针方向30°)2.径向渐变 background-image:radial-gra......
  • Hetao P1031 萌萌题 题解 [ 蓝 ] [ 线性 dp ]
    萌萌题:一道结合了观察性质的线性dp。观察我们先考虑极端情况:所有数相同,所有数降序排列两种情况。对于所有数相同的情况,我们发现,最终可以合并出来的区间,最多只有\(n\logn\)个。怎么证明?考虑固定右端点,那么我们想要合并出一个点,就得选\(2^k\)个数出来,这就有\(\logn\)......
  • 【数据结构与算法】线性表
    文章目录一.什么是线性表?二.线性表如何存储?三.线性表的类型我们知道从应用中抽象出共性的逻辑结构和基本操作就是抽象数据类型,然后实现其存储结构和基本操作。下面我们依然按这个思路来认识线性表一.什么是线性表?定义线性表(LinearList)是由n(n>=0)个具有相同特性......
  • 【机器学习】线性回归算法简介 及 数学实现方法
    线性回归简介利用回归方程(函数)对一个或多个自变量(特征值)和因变量(目标值)之间关系进行建模的一种分析方式。数学公式:ℎ_(w)=w_1x_1+w_2x_2+w_3x_3+…+b=w^Tx+b概念​利用回归方程(函数)对一个或多个自变量(特征值)和因变量(目标值)之间关系进......
  • 聚焦线性注意力
    一、本文介绍本文给大家带来的改进机制是FocusedLinearAttention(聚焦线性注意力)是一种用于视觉Transformer模型的注意力机制(但是其也可以用在我们的YOLO系列当中从而提高检测精度),旨在提高效率和表现力。其解决了两个在传统线性注意力方法中存在的问题:聚焦能力和特征多样性。......
  • torch神经网络--线性回归
    简单线性回归y=2*x+1importnumpyasnpimporttorchimporttorch.nnasnnclassLinearRegressionModel(nn.Module):def__init__(self,input_dim,output_dim):super(LinearRegressionModel,self).__init__()self.linear=nn.Linear(input......
  • 【AI学习】Mamba学习(二):线性注意力
    上一篇《Mamba学习(一):总体架构》提到,Transformer模型的主要缺点是:自注意力机制的计算量会随着上下文长度的增加呈平方级增长。所以,许多次二次时间架构(指一个函数或算法的增长速度小于二次函数,但大于线性函数),如线性注意力、门控卷积和循环模型,以及结构化状态空间模型(SSM)被......
  • 用Python实现运筹学——Day 9: 线性规划的灵敏度分析
    一、学习内容1.灵敏度分析的定义与作用灵敏度分析(SensitivityAnalysis)是在优化问题中,分析模型参数变化对最优解及目标函数值的影响。它帮助我们了解在线性规划模型中,当某些参数(如资源供应量、成本系数等)发生变化时,最优解是否会发生变化,以及这种变化的幅度。灵敏度分析的......
  • 用Python实现运筹学——Day 10: 线性规划的计算机求解
    一、学习内容1.使用Python的scipy.optimize.linprog进行线性规划求解scipy.optimize.linprog是Python中用于求解线性规划问题的函数。它实现了单纯形法、内点法等算法,能够处理求解最大化或最小化问题,同时满足线性约束条件。线性规划问题的形式:线性规划问题可以描......
  • 网络流与线性规划24题详解(上)
    前言题单刷24题刷魔怔了,写个详解。难度不断递增,T1-T9为蓝题,T10-T23为紫题。(什么?你问我为什么没有T24?)好了,让我们开始吧!T1孤岛营救问题思路:这题数据小,所以用BFS\(key[x][y][k]\)记录\((x,y)\)的第k把钥匙\(wall[x1][y1][x2][y2]\)记录墙和门\(vis[x1][y1][k]\)记录是否走......