[ABC216H] Random Robots

一、题目

点此看题

\(n\) 个机器人排成一排,有 \(m\) 个时刻,每个时刻每个机器人有 \(1/2\) 的概率向右走一步,有 \(1/2\) 的概率在原地不动。初始第 \(i\) 个机器人在位置 \(x_i\),问所有机器人不相撞的概率,答案模 \(998244353\)

\(n\leq 10,x_i,m\leq 1000\)

二、解法

首先转成不相撞的方案数,最后再除以 \(2^{nm}\)

要求不相撞的方案数可以考虑一个逆序对容斥,我们枚举最终位置序列 \(y_i\)

\[\sum_{y}(-1)^{\pi(y)}\prod_{i=1}^n{m\choose y_i-x_i} \]

解释一下这个容斥,如果两个机器人重合那么我们就交换它们的操作序列,这个交换操作会使得逆序对减少 \(1\),其实这和 \(\tt LGV\) 引理差不多。那么最后只会把没有逆序对的方案算进答案里,也就是所有机器人不相撞的方案数。

现在考虑怎么计算这个容斥式子,主要问题在于确定序列 \(y\),由于数据范围很小可以考虑 \(dp\),具体来说我们最外层从小到大枚举最终位置 \(y\),然后考虑向状态 \(s\) 中加入最终位置为 \(y\) 的某个点,已经在集合中的最终位置都会比当前枚举的最终位置小,所以这时候很方便统计逆序对数,时间复杂度 \(O(n2^nm)\)

三、总结

路径不相交问题首选逆序对容斥,然后分为两种情况:如果终点确定那么可以套用 \(\tt LGV\) 引理;如果数据范围小那么可以 \(dp\) 计算容斥系数。

状压 \(dp\) 也可用于确定序列问题上,只是这是时候需要多消耗 \(O(\)值域\()\) 的复杂度。

#include <cstdio>
#include <algorithm>
using namespace std;
const int M = 2005;
const int MOD = 998244353;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,a[M],f[M],p[1<<11],dp[1<<11];
int qkpow(int a,int b)
{
	int r=1;
	while(b>0)
	{
		if(b&1) r=r*a%MOD;
		a=a*a%MOD;
		b>>=1;
	}
	return r;
}
signed main()
{
	n=read();m=read();
	for(int i=0;i<n;i++)
		a[i]=read();
	f[0]=dp[0]=1;
	for(int i=1;i<=m;i++)
		f[i]=f[i-1]*(m-i+1)%MOD*qkpow(i,MOD-2)%MOD;
	for(int i=0;i<(1<<n);i++)
		p[i]=__builtin_parity(i)?-1:1;
	for(int i=0;i<=a[n-1]+m;i++)
		for(int s=(1<<n)-1;s>=0;s--)
			for(int j=0;j<n;j++) if(!(s&(1<<j)))
				if(a[j]<=i && i<=a[j]+m)
				{
					dp[s|(1<<j)]=(dp[s|(1<<j)]
					+p[s>>j]*dp[s]*f[i-a[j]])%MOD;
				}
	int ans=dp[(1<<n)-1]*qkpow((MOD+1)/2,n*m)%MOD;
	printf("%lld\n",(ans+MOD)%MOD);
}
posted @ 2021-08-30 14:53  C202044zxy  阅读(299)  评论(2编辑  收藏  举报