题解:P11292 【MX-S6-T4】「KDOI-11」彩灯晚会

彩灯晚会:

\(n\)\(m\)\(k\) 种颜色,给每个点染色。
\(cnt_i\):第 \(i\) 种颜色长度为 \(l\) 的链的数量。其中 \(l\) 为题目给的一个常量。
\(\sum_{染色方案}\sum_{i=1}^k cnt_i^2\) 的和。


\(\sum_{染色方案}cnt_i\) 值都一样,钦定 \(pos\) 作为代表颜色,那么:
\(\sum_{染色方案}\sum_{i=1}^k cnt_i^2=k×\sum_{染色方案}cnt_{pos}^2\)

如果不解决 \(cnt_{pos}^2\),那么我们不得不枚举染色方案,所以考虑解决 \(cnt_{pos}^2\)
思考图论意义,\(cnt_{pos}^2=(1+1+…+1+1)×(1+1+…+1+1)\)
手玩可得:\(cnt_{pos}^2\) 为选择两个颜色为 pos 长度为 l 的链的方案数。

已经到可以 DP 的程度了。
\(f_{u,len1,len2,p}\)\(u\) 作为第 \(p\) 个重合点,链一长度为 \(len1\),链二长度为 \(len2\) 的方案数。
这时候发现转移很难,因为为了维护 DP 含义,我们不得不使两个链到下一个重合点前没有重合点。
用二项式反演把 DP 含义从“恰好”改为“钦定”就好了!

  • 具体的(\(F(i)\) 表示“钦定”,\(G(i)\) 表示“恰好”:
    \(F(i)=\sum_{i=1}^n k×f_{u,l,l,i}×k^{n-2l+i}\)(第一个 \(k\)\(pos\) 的值域)
    \(G(i)=\sum_{j=i}^l (-1)^{j-i} \binom{j}{i} F(j)\)

\(dis_{u,v,len}\):从 \(u\)\(v\) 长度为 \(len\) 的路径数。
\(st_{u,len}\)\(u\) 开头长度为 \(len\) 的路径条数。
\(ed_{u,len}\)\(u\) 结尾长度为 \(len\) 的路径条数。

\(O(n^3l+n^2l^5)\),其中 \(n^2l^5\) 中的一个 \(n\) 跑不满(DAG)导致 \([76,84]\) 分,拿上直接变人上人。

trick:分步枚举

  • 具体的:
    \(cab_{u,len1,len2,p}\):当前 \(len1\) 转移完了的 \(f\) 数组。
    \(f_{u,len1,len2,p}\):完整转移后的 \(f\) 数组。
    先枚举 \(len1\)\(f_{u,len1,len2,p}\) 更新 \(cab_{v,len1+k,len2,p+1}\)
    再枚举 \(len2\)\(cab_{u,len1,len2,p}\) 更新 \(f_{v,len1,len2+k,p+1}\)

\(O(n^3l+n^2l^4)\)——\([84,92]\) 分,考场知足分。

宇宙射线:把计算公式代入二项式反演的式子里化简。

\(H(i)=\sum_{i=1}^n f_{u,l,l,i}\)
\(F(i)=k^{n-2l+i+1}H(i)\)(第一个 \(k\)\(pos\) 的值域)
\(G(i)=\sum_{j=i}^l (-1)^{j-i} \binom{j}{i} F(j)\)

\[\begin{aligned} ans&=\sum_{i=0}^l G(i)\\ &=\sum_{i=0}^l \sum_{j=i}^l (-1)^{j-i} \binom{j}{i} k^{n-2l+i+1} H(j)\\ &=k^{n-2l+1}\sum_{j=0}^lH(j) \sum_{i=0}^j\binom{j}{i} (-1)^{j-i}k^i\\ &=k^{n-2l+1}\sum_{j=0}^l(k-1)^jH(j) \end{aligned} \]

trick:\((k-1)^j\)\(H(j)\) 的次数一样,可以直接把 \((k-1)\) 下发到 \(H(j)→H(j+1)\) 的转移,这时无须记录重合点个数了,直接一起计算就行了。

\(O(n^3l+n^2l^3)\)——\(100\) 分,卡常就不说了。

美码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int id,n,k,l,m; 
struct bbb {int v,w;} ;
const int QAQ=310,mo=998244353,ovo=22;
int idfn[QAQ],rd[QAQ],cab[QAQ][ovo][ovo],ed[QAQ][ovo],st[QAQ][ovo];
int f[QAQ][ovo][ovo],dis[QAQ][QAQ][ovo];
vector<bbb> dian[QAQ];
queue<int> dui;
int cnt;
__int128 ans;
#define jia(x,y) x=(x+y)%mo
int ksm(int x,int k)
{
	int da=1;
	for(;k;k>>=1,x=x*x%mo) if(k&1) da=da*x%mo;
	return da;
}
void write(__int128 ans)
{
	if(ans==0) return ;
	write(ans/10);
	putchar(ans%10+'0');
}
signed main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>id>>n>>k>>l>>m;
	for(int i=1;i<=m;i++)
	{
		int u,v,w;
		cin>>u>>v>>w;
		dian[u].push_back({v,w});
		rd[v]++;
	}
	for(int i=1;i<=n;i++) if(rd[i]==0) dui.push(i);
	while(!dui.empty())
	{
		int x=dui.front();dui.pop();
		++cnt,idfn[cnt]=x;
		for(bbb nw:dian[x])
		{
			rd[nw.v]--;
			if(rd[nw.v]==0) dui.push(nw.v);
		}
	}
	for(int i1=1;i1<=n;i1++)
	{
		int i=idfn[i1];
		dis[i][i][1]=1;
		for(int i2=i1;i2<=n;i2++)
			for(int len=1;len<l;len++)
			{
				int j=idfn[i2];
				for(bbb nw:dian[j])
					jia(dis[i][nw.v][len+1],dis[i][j][len]*nw.w);
			}
	}
	for(int i=1;i<=n;i++)
		for(int k=1;k<=n;k++)
			for(int j=1;j<=l;j++)
				jia(st[i][j],dis[i][k][j]),
				jia(ed[i][j],dis[k][i][j]);
	
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			ans+=dis[i][j][l];
	ans%=mo;
	ans=ans*ans%mo;
	
	for(int i=1;i<=n;i++)
	{
		int u=idfn[i];
		for(int o1=1;o1<=l;o1++)
			for(int o2=1;o2<=l;o2++)
				jia(f[u][o1][o2],ed[u][o1]*ed[u][o2]%mo*(k-1));
		for(int k=1;k<=n;k++)
			for(int i=1;i<=l;i++)
				for(int j=1;j<=l;j++)
					cab[k][i][j]=0;
		for(int i2=i;i2<=n;i2++)
			for(int o1=1;o1<=l;o1++)
				for(int o2=1;o2<=l;o2++)
				{
					int v=idfn[i2];
					for(int c=1;c<=l-o1;c++)
						jia(cab[v][o1+c][o2],f[u][o1][o2]*dis[u][v][c+1]);
				}
		for(int i2=i;i2<=n;i2++)
			for(int o1=1;o1<=l;o1++)
				for(int o2=1;o2<=l;o2++)
				{
					int v=idfn[i2];
					for(int c=1;c<=l-o2;c++)
						jia(f[v][o1][o2+c],cab[v][o1][o2]*dis[u][v][c+1]%mo*(k-1));
				}
	}
	for(int i=1;i<=n;i++)
		for(int o1=1;o1<=l;o1++)
			for(int o2=1;o2<=l;o2++)
				ans+=f[i][o1][o2]*st[i][l-o1+1]%mo*st[i][l-o2+1]%mo;
	ans%=mo;
	if(n-2*l+1>=0) ans=ans*ksm(k,n-2*l+1)%mo;
	else ans=ans*ksm(ksm(k,mo-2),-(n-2*l+1))%mo;
	if(ans==0) cout<<"0";
	else write(ans);
	return 0;
}
posted @ 2025-09-17 22:57  _a1a2a3a4a5  阅读(8)  评论(0)    收藏  举报