【学习笔记】[ABC323G] Inversion of Tree

已知nkessi一篇博客写50道题,而我的一篇博客写1道题,那么祂写博客的数量等价于我的多少篇博客?

前置知识:矩阵树定理特征多项式

省流:板子缝合题。可以复习一下线性代数的基本知识。

定义Pu>Pv的边价值为x,$P_u

暴力做法是O(n4)的。所以需要一点科技。

考虑特征多项式。对于一个n×n的矩阵A,定义它的特征多项式为:

pA(x)=det(xInA)

其中In是一个n阶的单位矩阵,最后的pA(x)是一个n次多项式。

如果一个矩阵次对角线下方的位置全部都是0,那么把其称为上海森堡矩阵。

任意n级矩阵的特征多项式都能在O(n3)的时间复杂度内求出,方法:

1.1 利用相似矩阵特征行列式相同的性质将给定矩阵消成上海森堡矩阵 这是因为对于初等可逆矩阵P,有: det(xInA)=det(xInPAP1)

这个东西 好像见过,当时不知道有啥用。。。(其实用处还挺大的,可以加速 矩阵快速幂,但是手算的部分挺难的。。。)

考虑高斯消元的三个操作:

  • 交换两行
  • 将第i行的k倍加到第j行上
  • 将第i行的数乘上k

它们都能写成一个矩阵P。也就是说,每次消元的过程可以看成左乘上矩阵P,而P1不难手算得到,并且右乘上P1等价于是对列进行操作,因此在消元的过程中,当我们对行进行操作时,要同时对列执行其共轭操作。

1.2 使用行列式展开定理递推计算它的特征多项式 记pi(x)表示左上角i×i的矩阵对应的特征多项式。则递推式 ~~可以一通手算得到~~ : pi(x)=xpi1(x)m=1iAm,i(j=m+1iAj,j1)pm1(x)

特别的,p0(x)=1

对于这道题,考虑将L分解成:

L=A+xB

然后同时对A,B进行消元操作,即:

L=A+xI

最后套用上述做法即可。

注意到第一步过程中如果有一列被消完了那么就将这一列乘上x(等价于将A的这一列搬到B上),如果乘x的次数>n就寄了。

因为都是板子,所以建议多看一下代码。

注意行和列都要进行操作。

复杂度O(n3)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define fi first
#define se second
using namespace std;
const int N=505;
const int mod=998244353;
int n,p[N];
ll A[N][N],B[N][N],dp[N][N],res[N];
ll fpow(ll x,ll y=mod-2){
ll z(1);
for(;y;y>>=1){
if(y&1)z=z*x%mod;
x=x*x%mod;
}return z;
}
void add(ll &x,ll y){
x=(x+y)%mod;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;for(int i=0;i<n;i++)cin>>p[i];
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(p[i]>p[j]){
A[i][i]++,A[j][j]++,A[i][j]--,A[j][i]--;
}
else{
B[i][i]++,B[j][j]++,B[i][j]--,B[j][i]--;
}
}
}
n--;
int mul=0;
ll coef=1;
for(int i=0;i<n;i++){
for(int j=i;j<n;j++){
if(A[j][i]!=0){
swap(A[i],A[j]);
swap(B[i],B[j]);
if(i!=j)coef*=-1;
break;
}
}
while(A[i][i]==0){
for(int j=0;j<n;j++){
A[j][i]=B[j][i];
B[j][i]=0;
}mul++;
if(mul>n){
for(int i=0;i<=n;i++)cout<<0<<"\n";
return 0;
}
for(int j=0;j<i;j++){
if(A[j][i]!=0){
ll x=A[j][i];
for(int k=0;k<n;k++){
add(A[k][i],-A[k][j]*x);
add(B[k][i],-B[k][j]*x);
}
}
}
for(int j=i;j<n;j++){
if(A[j][i]!=0){
swap(A[i],A[j]);
swap(B[i],B[j]);
}
if(i!=j){
coef*=-1;
}
break;
}
}
ll inv=fpow(A[i][i]);
coef=coef*A[i][i]%mod;
for(int j=0;j<n;j++){
A[i][j]=A[i][j]*inv%mod;
B[i][j]=B[i][j]*inv%mod;
}
for(int j=i+1;j<n;j++){
if(A[j][i]!=0){
ll x=A[j][i];
for(int k=0;k<n;k++){
add(A[j][k],-x*A[i][k]);
add(B[j][k],-x*B[i][k]);
}
}
}
for(int j=i+1;j<n;j++){
if(A[i][j]!=0){
ll x=A[i][j];
for(int k=0;k<n;k++){
add(A[k][j],-x*A[k][i]);
add(B[k][j],-x*B[k][i]);
}
}
}
}
for(int i=0;i<n-1;i++){
int p=-1;
for(int j=i+1;j<n;j++){
if(B[j][i]!=0){
p=j;
break;
}
}
if(p==-1)continue;
if(p!=i+1){
for(int j=0;j<n;j++)swap(B[i+1][j],B[p][j]);
for(int j=0;j<n;j++)swap(B[j][i+1],B[j][p]);
}
for(int j=i+2;j<n;j++){
if(B[j][i]!=0){
ll x=B[j][i]*fpow(B[i+1][i])%mod;
for(int k=0;k<n;k++)add(B[j][k],-B[i+1][k]*x);
for(int k=0;k<n;k++)add(B[k][i+1],B[k][j]*x);
}
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
B[i][j]=-B[i][j];
}
}
dp[0][0]=coef;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
dp[i+1][j+1]=dp[i][j];
}
ll v=1;
for(int j=i;j>=0;j--){
for(int k=0;k<=i+1;k++){
add(dp[i+1][k],-v*dp[j][k]%mod*B[j][i]);
}if(j)v=v*B[j][j-1]%mod;
}
}
for(int i=mul;i<=n;i++)res[i-mul]=dp[n][i];
for(int i=0;i<=n;i++)cout<<(res[i]+mod)%mod<<" ";
}

【学习笔记】[ABC323G] Inversion of Tree

https://duanyu.netlify.app/2023/10/20/ABC323G/

Author

duanyu

Posted on

2023-10-20

Updated on

2023-11-05

Licensed under

Comments