已知nkessi一篇博客写50道题,而我的一篇博客写1道题,那么祂写博客的数量等价于我的多少篇博客?
前置知识:矩阵树定理,特征多项式
省流:板子缝合题。可以复习一下线性代数的基本知识。
定义Pu>Pv的边价值为x,$P_u
暴力做法是O(n4)的。所以需要一点科技。
考虑特征多项式。对于一个n×n的矩阵A,定义它的特征多项式为:
pA(x)=det(xIn−A)
其中In是一个n阶的单位矩阵,最后的pA(x)是一个n次多项式。
如果一个矩阵次对角线下方的位置全部都是0,那么把其称为上海森堡矩阵。
任意n级矩阵的特征多项式都能在O(n3)的时间复杂度内求出,方法:
1.1 利用相似矩阵特征行列式相同的性质将给定矩阵消成上海森堡矩阵
这是因为对于初等可逆矩阵
P,有:
det(xIn−A)=det(xIn−PAP−1)
这个东西 好像见过,当时不知道有啥用。。。(其实用处还挺大的,可以加速 矩阵快速幂,但是手算的部分挺难的。。。)
考虑高斯消元的三个操作:
- 交换两行
- 将第i行的k倍加到第j行上
- 将第i行的数乘上k
它们都能写成一个矩阵P。也就是说,每次消元的过程可以看成左乘上矩阵P,而P−1不难手算得到,并且右乘上P−1等价于是对列进行操作,因此在消元的过程中,当我们对行进行操作时,要同时对列执行其共轭操作。
1.2 使用行列式展开定理递推计算它的特征多项式
记
pi(x)表示左上角
i×i的矩阵对应的特征多项式。则递推式 ~~可以一通手算得到~~ :
pi(x)=x⋅pi−1(x)−i∑m=1Am,i(i∏j=m+1Aj,j−1)pm−1(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<<" "; }
|