zoukankan      html  css  js  c++  java
  • 逃亡

    ### Description

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

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

      

    Solution

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

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

      这样最后统计的时候就不用考虑那么多了,点(x)被经过的概率就是(1-prodlimits_{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)处停下的概率,那么这部分的结果为(sumlimits_{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)-2sumlimits_{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
    ",ans);
    }
    
  • 相关阅读:
    桥牌笔记:双挤
    打造GTD style的办公环境 V1.0
    《Two Dozen Short Lessons in Haskell》学习(四)
    跑步机到了,看能坚持多久
    《Two Dozen Short Lessons in Haskell》学习(十) Private Definitions — the whereclause
    赶在世界末日前完成的2012年全年总结
    《Two Dozen Short Lessons in Haskell》学习(三)
    《Two Dozen Short Lessons in Haskell》学习(六)
    《Two Dozen Short Lessons in Haskell》学习(二)
    桥牌笔记:进手张
  • 原文地址:https://www.cnblogs.com/yoyoball/p/10269238.html
Copyright © 2011-2022 走看看