一个新套路,
利用矩阵能将一些复杂的线性关系转换为一次矩阵乘法。
求解只需求区间积。
采用线段树(所以需要满足结合律)
更奇特的是你可以重载矩阵乘法时内部单个元素的乘法和加法(比如说改成取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]);
}
}