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;
}

posted @ 2025-04-17 17:00  Re_Star  阅读(33)  评论(0)    收藏  举报