题解:P11765 「KFCOI Round #1」回首

P11765 题解

题目描述不说了。

一开始觉得很像我之前做过的一道拆贡献的题,那题 \(n\le 10^5\),而这题竟然只到 \(200\),必有蹊跷。

发现对于第 \(i\) 次的操作,所有转移只和第 \(i-1\) 次有关,而 \(n\) 又如此小,考虑矩阵快速幂。

对于矩阵快速幂,可以参考我的学习笔记:再谈矩阵快速幂

考虑设状态矩阵,设 \(i\) 号点的权值为 \(a_i\)

发现状态矩阵必须包含三种信息,分别是每个点的权值、当前进行到多少轮和轮次自增量 \(1\)

所以考虑构造一个一行 \(n+2\) 列的状态矩阵如下:

\[\begin{bmatrix} a_1 & a_2 & \cdots & a_n & t & 1\end{bmatrix} \]

然后考虑构造转移矩阵,设为 \(fac\)。接下来描述可能比较抽象,建议结合下文的矩阵结果一起看。

首先因为状态矩阵是 \(1\times (n+2)\) 的,所以转移矩阵的大小必然是 \((n+2)\times (n+2)\) 的。

观察转移式子,发现每一次操作中,所有 \(a_i\) 都得乘上 \(k_i\)。所以对于 \(1\le i\le n\)\(fac_{i,i}=k_i\)

然后考虑处理边。

显然对于一条 \(u\rightarrow v\) 的边,\(v\) 会在转移中加上上个阶段 \(u\) 的权值。

\(c_{i,j}\) 表示是否存在一条 \(i\rightarrow j\) 的边。

结合矩阵乘法计算公式,故 \(fac_{u,v}=1\)。这里不懂可以自己手玩一下样例。

然后考虑构造转移矩阵中更新 \(t\) 的部分。显然只要令 \(t\leftarrow t+1\),即 \(fac_{n+2,n+2}=fac_{n+2,n-1}=fac_{n+1,n+1}=1\) 即可。

然后考虑没有入度的点需要加 \(t\) 的问题。

考虑若点 \(i\) 没有入度,则 \(fac_{n+1,i}=1\),反之为 \(0\)

这样若没有入度则加上 \(t\),反之不加。

然后没有提到的其余地方全部填 \(0\) 即可。

所以转移矩阵就被我们构造出来了,设 \(g_i\) 表示点 \(i\) 是否没有入度:

\[\begin{bmatrix} k_1 & c_{1,2} & \cdots & c_{1,n} & 0 & 0\\ c_{1,2} & k_2 & \cdots & c_{2,n} & 0 & 0\\ \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ c_{n,1} & c_{n,2} & \cdots & k_n & 0 & 0 \\ g_1 & g_2& \cdots & g_n & 1 & 0\\ 0 & 0 & \cdots & 0 & 1 & 1 \end{bmatrix} \]

然后就是简单的代码环节了,不懂的请看代码理解。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ljl;
const ljl Mod=998244353;
const int N=105,M=305;
int n,m,cnt_e,ehead[N],deg[N];
ljl T,k[N],g[N][N];
bool ccc[N];
struct MTX{
	ljl v[N][N];
	MTX(int o)
	{
		memset(v,0,sizeof(v));
		for(int i=1;i<N;++i)v[i][i]=o;
	}
}bs(1),f(0),fac(0);
MTX operator * (MTX a,MTX b)
{
	MTX ans(0);
	for(int i=1;i<=n+2;++i)
		for(int j=1;j<=n+2;++j)
			for(int k=1;k<=n+2;++k)
				ans.v[i][j]=(ans.v[i][j]+a.v[i][k]*b.v[k][j]%Mod)%Mod;
	return ans;
}
MTX qpow(MTX a,ljl k)
{
	MTX ans(1);
	while(k)
	{
		if(k&1)ans=ans*a;
		a=a*a;k>>=1;
	}return ans;
}
void printfac()
{
	for(int i=1;i<=n+2;++i)
	{
		for(int j=1;j<=n+2;++j)
			cout<<fac.v[i][j]<<' ';
		cout<<'\n';
	}
	return;
}
void outf()
{
	for(int i=1;i<=n;++i)
		cout<<f.v[1][i]<<' ';
	cout<<'\n';
	return;
}
int main(){
	ios::sync_with_stdio(0);
	cin>>n>>m>>T;
	for(int i=1;i<=n;++i)cin>>k[i];
	for(int i=1,u,v;i<=m;++i)
	{
		cin>>u>>v;
		g[u][v]=1;ccc[v]=1;
	}
	for(int i=1;i<=n;++i)fac.v[i][i]=k[i];
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			if(i!=j)
				fac.v[i][j]=g[i][j];
	fac.v[n+2][n+2]=1;fac.v[n+2][n+1]=1;fac.v[n+1][n+1]=1;
	for(int i=1;i<=n;++i)
		fac.v[n+1][i]=(ccc[i]^1);
	f.v[1][n+2]=1;f.v[1][n+1]=1;
	fac=qpow(fac,T);f=f*fac;
	outf();
	return 0;
}
posted @ 2026-07-27 13:17  Atserckcn  阅读(9)  评论(0)    收藏  举报