这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
2020-2021:teams:legal_string:组队训练比赛记录:contest2 [2021/07/12 23:12] lgwza [题解] |
2020-2021:teams:legal_string:组队训练比赛记录:contest2 [2021/07/18 11:04] (当前版本) jxm2001 |
||
---|---|---|---|
行 95: | 行 95: | ||
enter((ss-s[i]+mod)%mod); | enter((ss-s[i]+mod)%mod); | ||
return 0; | return 0; | ||
+ | } | ||
+ | </code> | ||
+ | </hidden> | ||
+ | |||
+ | ===== D. 方阵的行列式 ===== | ||
+ | |||
+ | ==== 题意 ==== | ||
+ | |||
+ | 给定一个模 998244353 (质数)意义下的 $n\times n$ 的方阵和 $Q$ 个修改操作。每个修改操作都修改方阵中某个元素为一个新的值。在每个修改操作后,输出方阵的行列式。 | ||
+ | |||
+ | 数据范围: | ||
+ | |||
+ | $1\le n,Q\le 500$ | ||
+ | |||
+ | (修改第 $x$ 行第 $y$ 列的元素为 $z$)$1\le x,y\le n,0\le z<998244353$。 | ||
+ | |||
+ | ==== 题解 ==== | ||
+ | |||
+ | === 前置知识 === | ||
+ | |||
+ | - **Sherman-Morrison formula**: 假设 $A\in\mathbb{R}^{n\times n}$ 是可逆方阵,$u,v\in\mathbb{R}^n$ 是列向量,则 $A+uv^T$ 可逆当且仅当 $1+v^TA^{-1}u\ne 0$,此时有 $$ | ||
+ | (A+uv^T)^{-1}=A^{-1}-\frac{A^{-1}uv^TA^{-1}}{1+v^TA^{-1}u} | ||
+ | $$ 成立 | ||
+ | - **定义**:在 $n$ 阶行列式中,划去元 $a_{ij}$ 所在的第 $i$ 行与第 $j$ 列的元,剩下的元不改变原来的顺序所构成的 $n-1$ 阶行列式称为元 $a_{ij}$ 的**余子式**,记作 $M_{ij}$。 | ||
+ | - **定义**:$a_{ij}$ 的**代数余子式**:$A_{ij}=(-1)^{i+j}M_{ij}$。 | ||
+ | - **定理**:当 $|A|\ne 0$ 时,$A^{-1}=\dfrac{1}{|A|}A^*$,其中 $A^*$ 的第 $(i,j)$ 元为 $A$ 的第 $(j,i)$ 元的代数余子式 $A_{ji}$。$A^*$ 称为 $A$ 的伴随方阵。 | ||
+ | - **展开定理**:行列式 $\Delta$ 的值,等于它的任意一行各元分别乘以各自的代数余子式的乘积之和: $$ | ||
+ | \Delta=\sum_{j=1}^na_{ij}A_{ij}. | ||
+ | $$ 注意:在按行展开的公式 $$ | ||
+ | \Delta=a_{i1}A_{i1}+\cdots+a_{ij}A_{ij}+\cdots+a_{in}A_{in} | ||
+ | $$ 中,第 $i$ 行的各元的代数余子式 $A_{i1},\cdots,A_{in}$ 的值都由 $\Delta$ 中第 $i$ 行以外的元决定,与第 $i$ 行各元 $a_{i1},\cdots,a_{in}$ 无关。因此,在行列式 $\Delta$ 中将第 $i$ 行各元分别换成任意值 $x_1,\cdots,x_n$,得到的行列式 $\Delta(x_1,\cdots,x_n)$ 按第 $i$ 行展开式中的各代数余子式 $A_{i1},\cdots,A_{in}$ 仍与在 $\Delta$ 中相同。 | ||
+ | |||
+ | === 算法步骤 === | ||
+ | |||
+ | - 高斯消元法求 $A^{-1}$ 和 $|A|$,记 $A^{-1}=C=(c_{ij})$,复杂度 $O(n^3)$。 | ||
+ | - 执行修改操作,将 $(x,y)$ 元修改为 $z$,改变量记为 $delta=z-a_{xy}$,改变后的矩阵记为 $A'=(a'_{ij})$。 | ||
+ | - 计算修改后的行列式的值 $|A'|$,用定理 5 对第 $x$ 行按行展开来求,而由定理 4,$A'_{ij}=A_{ij}=|A|c_{ji}$,故 $\displaystyle |A'|=\sum_{j=1}^na'_{xj}|A|c_{jx}$。 | ||
+ | - 令 $|A|=|A'|$,并用定理 1 计算 $(A')^{-1}$:令 $u=(0,\cdots,0,1,0,\cdots,0)^T$(第 $x$ 个分量为 1,其它分量为 0 的列向量),$v=(0,\cdots,0,delta,0,\cdots,0)^T$ (第 $y$ 个分量为 $delta$,其它分量为 0 的列向量),即可在 $O(n^2)$ 内算出 $(A')^{-1}=(A+uv^T)=A^{-1}-\dfrac{A^{-1}uv^TA^{-1}}{1+v^TA^{-1}u}$。然后令 $C=A^{-1}=(A')^{-1},A=A'$。回到步骤 2,进行下一步修改操作,直至修改操作结束。 | ||
+ | |||
+ | 故总时间复杂度 $O(n^3+Qn^2)$。 | ||
+ | |||
+ | <hidden 查看代码> | ||
+ | <code cpp> | ||
+ | #include<bits/stdc++.h> | ||
+ | using namespace std; | ||
+ | const int N=505,mod=998244353; | ||
+ | int A[N][N],B[N][2*N],C[N][N],D[N][N],E[N][N],F[N][N]; | ||
+ | int qpow(int x,int y){ | ||
+ | int ret=1; | ||
+ | for(;y;x=1ll*x*x%mod,y>>=1) | ||
+ | if(y&1) ret=1ll*ret*x%mod; | ||
+ | return ret; | ||
+ | } | ||
+ | void Get_Inverse_1(int n,int& det){ | ||
+ | det=1; | ||
+ | memset(B,0,sizeof(B)); | ||
+ | for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) B[i][j]=A[i][j]; | ||
+ | for(int i=1;i<=n;i++) B[i][i+n]=1; | ||
+ | | ||
+ | int times=0; | ||
+ | for(int i=1;i<n;i++){ | ||
+ | if(B[i][i]==0){ | ||
+ | times++; | ||
+ | int row=-1; | ||
+ | for(int j=i+1;j<=n;j++) | ||
+ | if(B[j][i]!=0){ | ||
+ | row=j; | ||
+ | break; | ||
+ | } | ||
+ | if(row==-1) continue; | ||
+ | for(int k=i;k<=2*n;k++) swap(B[i][k],B[row][k]); | ||
+ | } | ||
+ | for(int j=i+1;j<=n;j++){ | ||
+ | int m=1ll*B[j][i]*qpow(B[i][i],mod-2)%mod; | ||
+ | for(int k=i;k<=2*n;k++){ | ||
+ | B[j][k]=(1ll*B[j][k]-1ll*m*B[i][k]%mod+mod)%mod; | ||
+ | } | ||
+ | } | ||
+ | det=1ll*det*B[i][i]%mod; | ||
+ | } | ||
+ | det=1ll*det*B[n][n]%mod; | ||
+ | if(times&1) det=(mod-det)%mod; | ||
+ | for(int i=n;i>=1;i--){ | ||
+ | int temp=B[i][i]; | ||
+ | temp=qpow(temp,mod-2); | ||
+ | B[i][i]=1; | ||
+ | for(int k=n+1;k<=2*n;k++) B[i][k]=1ll*B[i][k]*temp%mod; | ||
+ | int temp2=qpow(B[i][i],mod-2); | ||
+ | for(int j=i-1;j>=1;j--){ | ||
+ | int m=1ll*B[j][i]*temp2; | ||
+ | for(int k=n+1;k<=2*n;k++){ | ||
+ | B[j][k]=(1ll*B[j][k]-1ll*m*B[i][k]%mod+mod)%mod; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) C[i][j]=B[i][j+n]; | ||
+ | } | ||
+ | int C2[N],C3[N]; | ||
+ | void Get_Inverse_2(int n,int x,int y,int delta){ | ||
+ | int deno=(1+1ll*C[y][x]*delta%mod)%mod; | ||
+ | deno=qpow(deno,mod-2); | ||
+ | for(int i=1;i<=n;i++) C2[i]=C[i][x],C3[i]=C[y][i]; | ||
+ | for(int i=1;i<=n;i++){ | ||
+ | for(int j=1;j<=n;j++) | ||
+ | C[i][j]=(1ll*C[i][j]-1ll*C2[i]*delta%mod*C3[j]%mod*deno%mod+mod)%mod; | ||
+ | } | ||
+ | } | ||
+ | int main(){ | ||
+ | | ||
+ | int n,q; | ||
+ | scanf("%d%d",&n,&q); | ||
+ | for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&A[i][j]); | ||
+ | int det; | ||
+ | Get_Inverse_1(n,det); | ||
+ | while(q--){ | ||
+ | int x,y,z; | ||
+ | scanf("%d%d%d",&x,&y,&z); | ||
+ | int before=A[x][y]; | ||
+ | A[x][y]=z; | ||
+ | int ans=0; | ||
+ | for(int j=1;j<=n;j++){ | ||
+ | ans=(1ll*ans+1ll*A[x][j]*C[j][x]%mod*det%mod)%mod; | ||
+ | } | ||
+ | printf("%d\n",ans); | ||
+ | det=ans; | ||
+ | int delta=(z-before+mod)%mod; | ||
+ | Get_Inverse_2(n,x,y,delta); | ||
+ | } | ||
+ | return 0; | ||
} | } | ||
</code> | </code> | ||
行 164: | 行 293: | ||
</code> | </code> | ||
</hidden> | </hidden> | ||
- | |||
- | ===== D. 方阵的行列式 ===== | ||
- | |||
- | ==== 题意 ==== | ||
- | |||
- | 给定一个模 998244353 (质数)意义下的 $n\times n$ 的方阵和 $Q$ 个修改操作。每个修改操作都修改方阵中某个元素为一个新的值。在每个修改操作后,输出方阵的行列式。 | ||
- | |||
- | 数据范围: | ||
- | |||
- | $1\le n,Q\le 500$ | ||
- | |||
- | (修改第 $x$ 行第 $y$ 列的元素为 $z$)$1\le x,y\le n,0\le z<998244353$。 | ||
- | |||
- | ==== 题解 ==== | ||
- | |||
- | === 前置知识 === | ||
- | |||
- | - **Sherman-Morrison formula**: 假设 $A\in\mathbb{R}^{n\times n}$ 是可逆方阵,$u,v\in\mathbb{R}^n$ 是列向量,则 $A+uv^T$ 可逆当且仅当 $1+v^TA^{-1}u\ne 0$,此时有 $$ | ||
- | (A+uv^T)^{-1}=A^{-1}-\frac{A^{-1}uv^TA^{-1}}{1+v^TA^{-1}u} | ||
- | $$ 成立 | ||
- | - **定义**:在 $n$ 阶行列式中,划去元 $a_{ij}$ 所在的第 $i$ 行与第 $j$ 列的元,剩下的元不改变原来的顺序所构成的 $n-1$ 阶行列式称为元 $a_{ij}$ 的**余子式**,记作 $M_{ij}$。 | ||
- | - **定义**:$a_{ij}$ 的**代数余子式**:$A_{ij}=(-1)^{i+j}M_{ij}$。 | ||
- | - **定理**:当 $|A|\ne 0$ 时,$A^{-1}=\dfrac{1}{|A|}A^*$,其中 $A^*$ 的第 $(i,j)$ 元为 $A$ 的第 $(j,i)$ 元的代数余子式 $A_{ji}$。$A^*$ 称为 $A$ 的伴随方阵。 | ||
- | - **展开定理**:行列式 $\Delta$ 的值,等于它的任意一行各元分别乘以各自的代数余子式的乘积之和: $$ | ||
- | \Delta=\sum_{j=1}^na_{ij}A_{ij}. | ||
- | $$ 注意:在按行展开的公式 $$ | ||
- | \Delta=a_{i1}A_{i1}+\cdots+a_{ij}A_{ij}+\cdots+a_{in}A_{in} | ||
- | $$ 中,第 $i$ 行的各元的代数余子式 $A_{i1},\cdots,A_{in}$ 的值都由 $\Delta$ 中第 $i$ 行以外的元决定,与第 $i$ 行各元 $a_{i1},\cdots,a_{in}$ 无关。因此,在行列式 $\Delta$ 中将第 $i$ 行各元分别换成任意值 $x_1,\cdots,x_n$,得到的行列式 $\Delta(x_1,\cdots,x_n)$ 按第 $i$ 行展开式中的各代数余子式 $A_{i1},\cdots,A_{in}$ 仍与在 $\Delta$ 中相同。 | ||
====== 赛后总结 ====== | ====== 赛后总结 ====== | ||
jxm:感觉有一半的时间在 debug | jxm:感觉有一半的时间在 debug | ||
+ | |||
+ | wza:代码能力需要加强 |