逃亡

### Description

  数轴上有\(m\)个人,第\(i\)个人在\(x_i\),每一时刻每个人会等概率往左边或者右边移动一单位长度,求\(n\)秒之后期望有多少个点被经过,答案对\(998244353\)取模

  数据范围:反正就是\(n*m^2\)就好了(原来的那个范围给的太鬼畜了qwq)

  

Solution

  其实要求的就是每个点被经过的概率之和(因为每个点的贡献是\(1\)嘛)

  直接求不好下手,考虑对于每一个人\(i\)求出:点\(x\)没有被经过的概率,记为\(P_i(x)\)

  这样最后统计的时候就不用考虑那么多了,点\(x\)被经过的概率就是\(1-\prod\limits_{i=1}^m P_i(x)\)

  现在每个人的影响就独立了,考虑对于一个人怎么求\(P_i(x)\)

​  然后这个时候发现好像求\(x\)被经过的概率更加容易,所以我们转而求点\(x\)被第\(i\)个人经过的概率,\(P_i(x)\)的话直接用\(1\)减一下就出来了

  按照\(n\)秒后这个人停下的位置\(ed\)分类,一个点\(x\)被经过有三种情况:\(ed<x\)\(ed=x\)\(ed>x\)

  首先考虑\(x< ed\)的情况,记\(F(i)\)为这个人最后在\(i\)处停下的概率,那么这部分的结果为\(\sum\limits_{i=x+1}^{+\infty}F(i)\)(然而实际上当\(i\)与起点的距离大于\(n\)的时候\(F(i)=0\),所以只用计算到起点\(+n\)的位置就好了)

  然后看\(ed<x\)的情况,这部分看起来比较麻烦,因为我要走过\(x\)然后再折回来,这时候有一个很棒的处理方式:考虑以时间为\(x\)轴,位置为\(y\)轴建立坐标系,那么一种可行的方案画出来大概长这样:

  这个时候我们将第一次走到\(x\)的那个时刻之后的线段沿直线翻折上去,因为原来的终点\(ed\)\(<x\)的,所以翻折之后新的终点一定是\(>x\)的,然后用这种方式我们就将一种\(ed<x\)的方案与唯一一种\(ed>x\)的方案对应上了,我们可以用计算\(x<ed\)的方式计算

  再加上\(ed=x\)的情况,得到\(P_i(x)=1-(F(x)-2\sum\limits_{j=x+1}^{+\infty}F(j))\)

  所以只要预处理出\(F\)的后缀和以及每个人可能到达的点的并集就好啦ovo

  

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define pb push_back
using namespace std;
const int N=120000000,MOD=998244353,inf=2147483647;
struct seg{
	int l,r;
	seg(int _l=0,int _r=0){l=_l; r=_r;}
	bool merge(int l1,int r1){
		if (r1<l||r<l1) return 0;
		l=min(l,l1);
		r=max(r,r1);
		return 1;
	}
};
vector<seg> a;
int fac[N],invfac[N];
int sum[N];
int p[100010],st[100010];
int n,m,inv2;
int ans,L,R;
int mul(int x,int y){return 1LL*x*y%MOD;}
int plu(int x,int y){return (1LL*x+y)-(1LL*x+y>=MOD?MOD:0);}
int ksm(int x,int y){
	int ret=1,base=x;
	for (;y;y>>=1,base=mul(base,base))
		if (y&1) ret=mul(ret,base);
	return ret;
}
int C(int n,int m){return n<m?0:mul(fac[n],mul(invfac[m],invfac[n-m]));}
int F(int x){
	if ((x-n)&1) return 0;
	return mul(inv2,C(n,(n+x)/2));
}
void prework(int n){
	fac[0]=1;
	for (int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
	invfac[n]=ksm(fac[n],MOD-2);
	for (int i=n-1;i>=0;--i) invfac[i]=mul(invfac[i+1],i+1);
	int tmp=ksm(2,MOD-2);
	inv2=ksm(ksm(2,n),MOD-2);
	sum[n+1]=0;
	for (int i=n;i>=1;--i) sum[i]=plu(sum[i+1],F(i));
}
int solve(int id){
	int tmp,x,p=1;
	int ret=0;
	for (int i=a[id].l;i<=a[id].r;++i){
		p=1;
		for (int j=1;j<=m;++j){
			x=abs(st[j]-i);
			if (x>n) continue;
			tmp=plu(1,MOD-plu(F(x),mul(2,sum[x+1])));
			p=mul(p,tmp);
		}
		ret=plu(ret,plu(1,MOD-p));
	}
	ret+=ret<0?MOD:0;
	return ret;
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
#endif
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;++i) scanf("%d",st+i);
	sort(st+1,st+1+m);
	prework(n);
	int cnt=-1;
	for (int i=1;i<=m;++i){
		if (a.empty()) a.pb(seg(st[i]-n,st[i]+n)),++cnt;
		else{
			if (!a[cnt].merge(st[i]-n,st[i]+n))
				a.pb(seg(st[i]-n,st[i]+n)),++cnt;
		}
	}
	ans=0;
	for (int i=0;i<=cnt;++i)
		ans=plu(ans,solve(i));
	printf("%d\n",ans);
}
posted @ 2019-01-14 21:49  yoyoball  阅读(389)  评论(0编辑  收藏  举报