简化题意
构造一个长度为 (n imes m) 的合法括号序列。第 (i) 个位置上的左括号代价为 (a_{i mod n}),右括号代价为 (b_{i mod n})。求构造符合要求的括号序列的最小代价。
题解
首先,可以推出一个时间复杂度为 (O(n^2m)) 的dp。设 (dp_{i,j}) 表示前 (i) 个括号中有 (j) 个未匹配左括号时的最小代价,转移方程为 (dp_{i,j}=min(dp_{i-1,j-1}+a_i,dp_{i-1,j+1}+b_i)quad (ile ncdot m,jle n)) ,分别为第(i)个是左括号或右括号的情况。
关于 (jle n) 的证明:设前 (i) 个括号中有 (j (j>n)) 个未匹配的左括号,易得 (i>n) ,也就是可以覆盖至少一个 (n) 循环。而 ([1,i]) 间左括号下标模 (n) 的不同余数个数一定 (>frac{n}{2}) ,否则每个 (n) 循环中左括号个数均 (le frac{n}{2}) ,不会有无法匹配的左括号。而 ([i+1,n]) 中一定有 (j) 个无法匹配的右括号,用以与 ([1,i]) 中未匹配的左括号匹配。所以同理, ([i+1,n]) 间右括号下标模 (n) 的不同余数个数一定 (>frac{n}{2}) ,否则没有无法匹配的右括号。所以 ([1,i]) 的左括号与 ([i+1,n]) 的右括号模 (n) 的余数有交集,一定存在一对模 (n) 同余左右括号可以互换位置,且代价不变。
但是上述算法的时间不够优秀,考虑用矩阵快速幂加速dp。定义运算 ( imes) (不是矩阵乘法QwQ!), ((A imes B)_{i,j}=min(A_{i,k}+B_{k,j})) ( (A_{i,k}) 表示 (A) 矩阵的第 (i) 行第 (k) 个元素,下标从 (0) 开始)。为了可以使用矩快,需要证明 (A imes (B imes C)=(A imes B) imes C=D): (D_{i,j}=min(A_{i,k1}+min(B_{k1,k2}+C_{k2,j}))=min(min(A_{i,k1}+B_{k1,k2})+C_{k2,j})=min(A_{i,k1})+min(B_{k1,k2})+min(C_{k2,j})) 。而在本题中,易得转移矩阵: (egin{pmatrix}infty &b_{i\%n}&infty&cdots&infty \ a_{i\%n}&infty&b_{i\%n}&cdots&infty \ infty&a_{i\%n}&infty&cdots&infty \ cdots \ infty&infty&infty&cdots&infty end{pmatrix} imes egin{pmatrix} dp_{i-1,0} \ dp_{i-1,1} \ dp_{i-1,2} \ cdots \ dp_{i-1,n}end{pmatrix} = egin{pmatrix} dp_{i,0} \ dp_{i,1} \ dp_{i,2} \ cdots \ dp_{i,n}end{pmatrix}) ,第 (j) 行第 (j-1) 个为 (a_{i\%n}) ,第 (j) 行第 (j+1) 个为 (b_{i\%n}) ,其余为正无穷( (i\%n) 表示 (i) 模 (n) 的余数)。设 (M_i) 表示以 (a_i,b_i) 组成的转移矩阵,则进一步化简后的答案为 ((M_0 imes M_1 imes cdots imes M_{n-1})^m_{0,0}) 。矩阵快速幂求值即可,时间复杂度为 (O(n^3log_m)) 。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=25,inf=2e9+1;//dp最大值可到2e9
struct mat {int x[N][N];};
int a[N],b[N],n;
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0' || ch>'9') {if(ch=='-') w=-1; ch=getchar();}
while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
void init(mat &x)//初始化
{
for(int i=1;i<=n+1;i++)
for(int j=1;j<=n+1;j++) x.x[i][j]=inf;
}
mat cal(mat x,mat y)//本题定义矩阵运算
{
mat tmp; init(tmp);
for(int i=1;i<=n+1;i++)
for(int j=1;j<=n+1;j++)
for(int k=1;k<=n+1;k++) tmp.x[i][j]=min(tmp.x[i][j],x.x[i][k]+y.x[k][j]);
return tmp;
}
mat qpow(mat x,int y)//矩阵快速幂
{
mat tmp; init(tmp);
bool flag=0;
while(y)
{
if(y&1)
{
if(flag) tmp=cal(tmp,x);
else tmp=x;//第一个无需进行取min运算
flag=1;
}
x=cal(x,x); y>>=1;
}
return tmp;
}
signed main()
{
n=read(); int m=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++) b[i]=read();
mat res,tmp; init(tmp);//res:目标矩阵,tmp:转移矩阵
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n+1;j++) tmp.x[j][j-1]=a[i],tmp.x[j][j+1]=b[i];
if(i==1) res=tmp;//第一个无需进行取min运算
else res=cal(tmp,res);
}
res=qpow(res,m);
printf("%lld",res.x[1][1]);
return 0;
}