P11173 「CMOI R1」We Want To Run / Nilpotent 题解
首先我们把矩阵 \(A\) 看成一个图的邻接矩阵,那么 \(A^b_{u,v}\) 表示 \(u\rightarrow v\) 所有路径的边权乘积之和。因为边权都大于 \(0\),所以不会出现多条路径边权乘积加起来等于 \(0\),那么 \(A^b=O\) 当且仅当图中不存在经过 \(b\) 条边的路径。
如果图中有环,那么图中一定存在经过任意点数的路径,则 \(f(A)\) 等于 \(0\)。所以我们只需要考虑 DAG。如果一个图最长链经过 \(s\) 个点,那么它对答案的贡献
考虑 DP,我们把图分层,设 \(f_{i,j,k}\) 表示前 \(i\) 个点分成 \(j\) 层,最后一层有 \(k\) 个点的方案数,这里我们钦定每个点都至少和上一层的一个点有边。这个转移是容易的,我们枚举下一层选 \(p\) 个点,这一层中每个点和上一层需要有连边,所以方案数为 \((a^k-1)^p\),和前面其它点的连边没有限制,方案数为 \((a^{i-k})^p\),那么有如下转移:
\[f_{i,j,k}\times(a^k-1)^p\times(a^{i-k})^p\times{i+p\choose p}\rightarrow f_{i+p,j+1,p}
\]
但是这个转移总复杂度是 \(O(N^4)\),过不了。这里我们考虑第二维 \(j\) 的意义是什么,因为最终对答案的贡献是 \(j\times f_{i,j,k}\),所以我们才需要记录点的分层数。但是这里我们考虑乘的这个 \(j\) 的组合意义就是,将一层选作关键层的方案数,那么我们就不需要记录层数而只需要记录一维 \(0/1\) 表示是否选过关键层。新的状态为 \(f_{i,j,0/1}\) 表示前 \(i\) 个点分层,最后一层有 \(j\) 个点,是否选过关键层的方案数。这个转移和上面是类似的。
\[\begin{aligned}
\begin{equation}
\begin{cases}
f_{i,j,op}\times(a^j-1)^p\times(a^{i-j})^p\times{i+p\choose p}\rightarrow f_{i+p,p,op}\\
f_{i,j,0}\times(a^j-1)^p\times(a^{i-j})^p\times{i+p\choose p}\rightarrow f_{i+p,p,1}\\
\end{cases}
\end{equation}
\end{aligned}
\]
现在总复杂度是 \(O(N^3)\),可以过了。
code:
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=605,mod=202407031;
ll n,a,ans,c[N][N],dp[N][N][2],s1[N*N],s2[N][N];
inline ll rd()
{
char c;int f=1;
while(!isdigit(c=getchar()))if(c=='-')f=-1;
ll x=c-'0';
while(isdigit(c=getchar()))x=(x*10+(c^48))%mod;
return x*f;
}
inline ll qp(ll x,ll y)
{
ll res=1;
while(y)
{
if(y&1) (res*=x)%=mod;
(x*=x)%=mod,y>>=1;
}
return res;
}
int main()
{
for(int i=0;i<N;i++)
{
c[i][0]=c[i][i]=1;
for(int j=1;j<i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
n=rd(),a=rd();
for(int i=0;i<=n*n;i++) s1[i]=qp(a,i);
for(int i=0;i<N;i++)
for(int j=0;j<N;j++) s2[i][j]=qp(qp(a,i)-1+mod,j);
for(int i=1;i<=n;i++) dp[i][i][0]=dp[i][i][1]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
for(int k=0;k<2;k++) if(dp[i][j][k])
{
ll x=dp[i][j][k];
for(int p=1;p+i<=n;p++)
{
(dp[i+p][p][k]+=x*s2[j][p]%mod*s1[p*(i-j)]%mod*c[i+p][i]%mod)%=mod;
if(!k) (dp[i+p][p][1]+=x*s2[j][p]%mod*s1[p*(i-j)]%mod*c[i+p][i]%mod)%=mod;
}
}
for(int i=1;i<=n;i++) (ans+=dp[n][i][1])%=mod;
cout<<ans;
return 0;
}

浙公网安备 33010602011771号