CF351C Solution
简化题意
构造一个长度为 \(n\times 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 (i\le n\cdot m,j\le n)\) ,分别为第\(i\)个是左括号或右括号的情况。
关于 \(j\le 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。定义运算 \(\times\) (不是矩阵乘法QwQ!), \((A\times B)_{i,j}=min(A_{i,k}+B_{k,j})\) ( \(A_{i,k}\) 表示 \(A\) 矩阵的第 \(i\) 行第 \(k\) 个元素,下标从 \(0\) 开始)。为了可以使用矩快,需要证明 \(A\times (B\times C)=(A\times B)\times 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})\) 。而在本题中,易得转移矩阵: \(\begin{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} \times \begin{pmatrix} dp_{i-1,0} \\ dp_{i-1,1} \\ dp_{i-1,2} \\ \cdots \\ dp_{i-1,n}\end{pmatrix} = \begin{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\times M_1\times \cdots \times 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;
}