矩阵乘法套线段树(动态最大子段和问题)

130 篇文章 1 订阅
68 篇文章 0 订阅

一个新套路,

利用矩阵能将一些复杂的线性关系转换为一次矩阵乘法。

求解只需求区间积。

采用线段树(所以需要满足结合律)

更奇特的是你可以重载矩阵乘法时内部单个元素的乘法和加法(比如说改成取max之类的)

只要重载之后仍然满足结合律

我自己想了一个判断方法。

要A*(B*C)=(A*B)*C

令A*(B*C)=D,(A*B)*C=E

有D=E

Dij=Eij

sigma(sigma(Aik*Bkl)*Clj)=sigma(Aik*sigma(Bkl*Clj))=sigma(sigma( Aik*Bkl*Clj ))

Aik*sigma(Bkl*Clj)=sigma(Aik*Bkl*Clj)

令Aik=X,Bkl*Clj=Y

X*sigma(Y)=sigma(X*Y)

然后想一想就行了。

比如让 * 为加法, + 为取max(+为sigma内的符号)

可以做动态最大子段和问题。

sum[i] 为 以i结尾的最大子段和,ans[i] 为 i之前的最大子段和, seq[i] 为 i位置上的值。

有:

seq[i]       -inf          0                        sum[i-1]                   sum[i]

seq[i]         0            0            X          ans[i-1]        =         ans[i]

0                0            0                        0                              0

 

然后ans[n]就是答案

动态修改只需要将第一个矩阵存在线段树里(有结合律可以乱搞)

单点修改,区间查询

另外矩阵乘法和线段树是两个卡常数重灾区,第一个矩阵有5个位置一直为0,不用存,乘的时候也不用像矩阵乘法一样三个for循环,挑会对答案造成影响的乘(比如第一行第二列一直都是-inf,干脆不算)。

 

给定递推式 Xi=Ai*X(i-1)+Bi

操作1:修改某对(Ai,Bi)

操作2:求Xk

 

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define maxn 500005
#define mod 1000000007
#define lc now<<1
#define rc now<<1|1
using namespace std;

int n,m;


struct node
{
	int a,b;
	node(){}
	node(int a,int b):a(a),b(b){}
	node operator *(const node &nxt)const
	{
		return node(1ll*a*nxt.a%mod,(1ll*a*nxt.b%mod+b)%mod);
	}
}ele[maxn*10],tmp;

void insert(int now,int l,int r,int pos,node val)
{
	if(pos>r || pos<l) return;
	if(l==r)
	{
		ele[now]=val;
		return;
	}
	int mid=(l+r)>>1;
	insert(lc,l,mid,pos,val);
	insert(rc,mid+1,r,pos,val);
	ele[now]=ele[rc]*ele[lc];
}

node Query(int now,int l,int r,int ql,int qr)
{
	if(ql>r || qr<l) return node(1,0);
	if(ql<=l && r<=qr)
		return ele[now]; 
	int mid=(l+r)>>1;
	return Query(rc,mid+1,r,ql,qr)*Query(lc,l,mid,ql,qr);
}

int main()
{
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	int u;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n*5;i++) ele[i]=node(1,0);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&tmp.a,&tmp.b);
		insert(1,1,n,i,tmp);
	}
	
	char ch[10];
	for(int i=1;i<=m;i++)
	{
		scanf("%s",ch);
		if(ch[0]=='Q')
		{
			scanf("%d",&u);
			tmp=Query(1,1,n,1,u);
			printf("%d\n",(tmp.a+tmp.b)%mod);
		}
		else
		{
			scanf("%d",&u);
			scanf("%d%d",&tmp.a,&tmp.b);
			insert(1,1,n,u,tmp);
		}
	}
}
#include<bits/stdc++.h>
#define maxn 30005
#define LL long long
#define inf (1ll<<50)
/*

calc(i+1,i+1)	cc	cc	dp[i-1]  dp[i]
0				-oo	-oo	dp[i-2]  dp[i-1]
-oo				0	-oo	dp[i-3]  dp[i-2]

*/
using namespace std;

int n,q;
int w[maxn],r[maxn],c1[maxn],c2[maxn],loc[maxn],lc[maxn];
inline bool cmp1(const int &u,const int &v){ return w[u] > w[v]; }
inline bool cmp2(const int &u,const int &v){ return r[u] > r[v]; }
LL f[70000][3][3],M;
LL calc(int x,int y)
{	if(x<=0 || y<=0 || x>n || y>n) return -inf;
	return 1ll * w[c1[x]] * r[c2[y]]; }
inline void mt(int x,int u=0,int v=0)
{  
	u = x<<1|1 , v = x<<1;
	memcpy(f[x],f[0],sizeof f[0]);
	for(int i=0;i<3;i++)
		for(int j=0;j<3;j++) if(f[u][i][j] > -inf)
			for(int k=0;k<3;k++) if(f[v][j][k] > -inf)
				f[x][i][k] = max(f[x][i][k] , f[u][i][j] + f[v][j][k]);
}
void upd(int x){ for(x>>=1;x;x>>=1) mt(x); }

int main()
{
	scanf("%d%d",&n,&q);
	for(M=1;M<n;M<<=1);
	for(int i=1;i<=n;i++) scanf("%d",&w[i]),c1[i]=i;
	for(int i=1;i<=n;i++) scanf("%d",&r[i]),c2[i]=i;
	sort(c1+1,c1+1+n,cmp1),sort(c2+1,c2+1+n,cmp2);
	for(int i=1;i<=n;i++)
	{
		lc[c1[i]] = i;
		loc[c2[i]] = i;
		f[M+i-1][0][0] = (c1[i]!=c2[i] ? calc(i,i) : -inf);
		f[M+i-1][0][1] = calc(i,i-1)+calc(i-1,i);
		f[M+i-1][0][2] = max(calc(i,i-1)+calc(i-1,i-2)+calc(i-2,i),calc(i,i-2)+calc(i-2,i-1)+calc(i-1,i));
		f[M+i-1][1][1] = f[M+i-1][1][2] = f[M+i-1][2][0] = f[M+i-1][2][2] = -inf;
		upd(M+i-1);
	}
	f[0][0][1] = f[0][0][2] = f[0][1][0] = f[0][1][2]
	= f[0][2][0] = f[0][2][1] = -inf;
	for(int i=n+1;i<=M;i++) 
	{
		memcpy(f[M+i-1],f[0],sizeof f[0]);
		upd(M+i-1);
	}
	for(int u,v;q--;)
	{
		scanf("%d%d",&u,&v);
		swap(r[u],r[v]);
		swap(loc[u],loc[v]);
		c2[loc[u]] = u , c2[loc[v]] = v;
		
		f[M+loc[u]-1][0][0] = (c1[loc[u]]!=c2[loc[u]] ? calc(loc[u] , loc[u]) : -inf) , upd(M+loc[u]-1);
		f[M+loc[v]-1][0][0] = (c1[loc[v]]!=c2[loc[v]] ? calc(loc[v] , loc[v]) : -inf) , upd(M+loc[v]-1);
		
		printf("%lld\n",f[1][0][0]);
	}
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值