• 6515. 【GDOI2020模拟03.28】数一数(one)


    题目

    (n)(m)列的格子,每列的格子中有一个是(1),其它的都是(0)
    对于某一列,第(i)行是(1)的概率是(p_i)
    从任意点出发,每次从当前列走到下一列的同一行、上一行,或下一行。
    贡献是沿路的数字和。
    (f(m))为最大值的期望。计算(lim_{m o inf} frac{f(m)}{m})


    思考历程

    话说题目描述是错了,按照题目描述来就根本过不了样例……
    这个东西搞死了绝大多数人半天……
    了解真正题意之后,发现(n<=2)时输出(1),于是就有分了。


    正解

    题目求的是“最大值”的“期望”,而不是“期望”的“最大值”,这一点上本来就已经很恶心了。考虑假如每个格子上的数都确定的情况下怎么搞。
    这是个小学生dp问题:(g_{i,j})表示到((i,j))的最大贡献。
    换一种更好看的记法:对于每一列,将最大值找出来,然后将所有的值变成自己和最大值的差值。
    可以发现,差值的范围是([0,n-1])。这样一列的状态最多只有(n^n)种。
    用bfs将这些状态全部搜出来。搜出来发现状态种类数其实更少,不超过(500)
    建出一个图,每个点代表一个状态,边代表哪个状态有多少概率转移到哪些状态。

    (m)趋向于无限的时候,对于得到的第(m)列的状态,状态出现的概率是恒定的。
    既然是恒定的,不妨对于每个状态,根据它的前驱状态列个方程。
    不要忘了所有状态概率加起来为(1)
    集齐一堆方程之后解之,就可以知道每种状态出现的概率。

    接下来考虑计算这些状态的贡献。
    先说做法:对于某个状态,枚举下一列(1)的位置,然后看看这个状态能转移到下一列的(1)的位置中,有没有最大值。如果有,就计入贡献。
    感性地解释一下:由于是极限,所以我们并不关心之前产生的最大值是多少。我们只需要关心它在到达无限列的时候,进行一次操作的增量。(m)趋向于无穷时,将(f(m))看成一个类似于一次函数的东西,之前的最大值无论是多少只能看做是常数,但是下一步的增量可以看做一次项系数(到达无限的时候,增量不变),众所周知计算极限的时候只有最高次项是有意义的。


    代码

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 6
    #define ll long long
    #define mo 1000000007
    inline ll qpow(ll x,int y=mo-2){
    	ll res=1;
    	for (;y;y>>=1,x=x*x%mo)
    		if (y&1)
    			res=res*x%mo;
    	return res;
    }
    int n;
    int a[N];
    int pown[N+1];
    int id[46656],num,re[500];
    int q[46656];
    struct EDGE{
    	int to,p;
    	EDGE *las;
    } e[500*6];
    int ne;
    EDGE *last[500];
    inline void link(int u,int v,int p){
    	e[ne]={v,p,last[u]};
    	last[u]=e+ne++;
    }
    #define get(x,k) ((x)/pown[k]%n)
    inline void init(){
    	int t[6];
    	int head=1,tail=1;
    	q[1]=0;
    	id[0]=++num;
    	re[num]=0;
    	while (head<=tail){
    		int x=q[head++];
    		for (int i=0;i<n;++i){
    			for (int j=0;j<n;++j){
    				int mn=get(x,j);
    				if (j-1>=0)
    					mn=min(mn,get(x,j-1));
    				if (j+1<n)
    					mn=min(mn,get(x,j+1));
    				t[j]=(mn-(i==j));	
    			}
    			if (t[i]==-1)
    				for (int j=0;j<n;++j)
    					t[j]++;
    			int s=0;
    			for (int j=0;j<n;++j)
    				s+=pown[j]*t[j];
    			if (!id[s]){
    				id[s]=++num;
    				re[num]=s;
    				q[++tail]=s;
    			}
    			link(id[s],id[x],a[i]);
    		}
    	}
    }
    int eql[500][500],cnt;
    int p[500];
    void calc(){
    	for (int i=1;i<=num;++i){
    		int j=i;
    		for (;j<=num && eql[i][j]==0;++j);
    		if (i!=j)
    			for (int k=0;k<=num;++k)
    				swap(eql[i][k],eql[j][k]);
    		ll invi=qpow(eql[i][i]);
    		for (int k=0;k<=num;++k)
    			eql[i][k]=(eql[i][k]*invi)%mo;
    		for (++j;j<=num;++j){
    			ll c=eql[j][i];
    			for (int k=0;k<=num;++k)
    				eql[j][k]=((eql[j][k]-c*eql[i][k])%mo+mo)%mo;
    		}
    	}
    	for (int i=num;i>=1;--i){
    		ll s=eql[i][0];
    		for (int j=i+1;j<=num;++j)
    			s=((s-(ll)eql[i][j]*p[j])%mo+mo)%mo;
    		p[i]=s;
    	}
    }
    int main(){
    	freopen("one.in","r",stdin);
    	freopen("one.out","w",stdout);
    	scanf("%d",&n);
    	pown[0]=1;
    	for (int i=1;i<=n;++i)
    		pown[i]=pown[i-1]*n;
    	for (int i=0;i<n;++i){
    		double x;
    		scanf("%lf",&x);
    		a[i]=int(x*10)*qpow(10)%mo;
    	}
    	init();
    	++cnt;
    	eql[cnt][0]=1;
    	for (int i=1;i<=num;++i)
    		eql[cnt][i]=1;
    	for (int i=2;i<=num;++i){
    		++cnt;
    		eql[cnt][i]=mo-1;
    		for (EDGE *ei=last[i];ei;ei=ei->las)
    			(eql[cnt][ei->to]+=ei->p)%=mo;
    	}
    	calc();
    	ll ans=0;
    	for (int i=1;i<=num;++i){
    		ll s=0;
    		for (int j=0;j<n;++j)
    			if (get(re[i],j)==0 || (j-1>=0 && get(re[i],j-1)==0) || (j+1<n && get(re[i],j+1)==0))
    				s+=a[j];
    		s%=mo;
    		ans=(ans+s*p[i])%mo;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
    

    总结

    真是……
    败在数学。

  • 相关阅读:
    array and ram
    char as int
    pointer of 2d array and address
    Install SAP HANA EXPRESS on Google Cloud Platform
    Ubuntu remount hard drive
    Compile OpenSSL with Visual Studio 2019
    Install Jupyter notebook and tensorflow on Ubuntu 18.04
    Build OpenCV text(OCR) module on windows with Visual Studio 2019
    Reinstall VirtualBox 6.0 on Ubuntu 18.04
    Pitfall in std::vector<cv::Mat>
  • 原文地址:https://www.cnblogs.com/jz-597/p/12608088.html
Copyright © 2020-2023  润新知