zoukankan      html  css  js  c++  java
  • 「CF722E Research Rover」

    题目大意

    给出一张 (ncdot m) 的图,图上有 (k) 个特殊点,在初始点有一个贡献 (t) 每经过一个特殊点会导致贡献变为 (lceilfrac{t}{2} ceil).求到 ((n,m)) 时的期望贡献.

    分析

    (n,m) 的范围都有 (10^5) 显然不能 (mathcal{O}(nm)) 的 dp,考虑只会在特殊点时贡献才会改变,所以用 (f_{i,j}) 表示终点在第 (i) 个点,总共经过了 (j) 个点的方案数.这个东西看起来是 (k^2) 的,而且转移也需要 (mathcal{O}(k)),时间复杂度 (mathcal{O}(k^3)) 不能过.但是一个数经过了 (log) 次除二向上取整后就会变成 (1),所以对于 (f_{i,j}(log_2t<j)) 的方案是没有用的,所以时间复杂度就变成了 (mathcal{k^2log_2t}) 可以通过本题.

    下面考虑如何转移 (f_{i,j}),因为在计算 (x_i,y_i) 这个点时需要把所以 (x_jleq x_iland y_jleq y_i) 的点全部计算出来,所以要先对所有点按 (x) 的大小((y) 的大小)排序.但是这个 (f_{i,j}) 的转移好像还是很麻烦,不如把 (f_{i,j}) 所代表的意义改成到第 (i) 个点时至少经过 (j) 个特殊点的方案数.那么转移方程就是:

    [f_{i,j}=sum_{kin[1,i]land x_kleq x_iland y_kleq y_i}C^{x_i+y_i-x_k-y_k+2}_{x_i-x_k+1}cdot f_{k,j-1} ]

    对于第 (i) 个特殊点经过恰好 (j) 次的方案数就是 (f_{i,j}-f_{i,j+1}).

    然后会发现第一个样例就过不去.

    在最后的贡献中 ((1,1) o(2,1) o(2,2) o(2,3) o(3,3)) 会计算两次,但是如果 (f_{k,j}) 表示的是第 (k) 个点时恰好经过 (j) 个特殊点的方案数,那么是对的.所以需要在每次计算出至少的方案数后直接计算出恰好的方案数.

    代码

    #include<bits/stdc++.h>
    #define REP(i,first,last) for(int i=first;i<=last;++i)
    #define DOW(i,first,last) for(int i=first;i>=last;--i)
    using namespace std;
    const int MAXN=2005;
    const int MAXNUM=1e6+5;
    const long long MOD=1e9+7;
    long long Pow(long long a,long long b,long long mod=MOD)
    {
    	long long result=1;
    	while(b)
    	{
    		if(b&1)
    		{
    			result*=a;
    			result%=mod;
    		}
    		a*=a;
    		a%=mod;
    		b/=2;
    	}
    	return result;
    }
    #define INV(a) Pow(a,MOD-2)
    long long fac[MAXNUM],inv[MAXNUM];
    void PreCom()
    {
    	fac[0]=1;
    	REP(i,1,MAXNUM-1)
    	{
    		fac[i]=fac[i-1]*i%MOD;
    	}
    	inv[MAXNUM-1]=INV(fac[MAXNUM-1]);
    	DOW(i,MAXNUM-2,0)
    	{
    		inv[i]=inv[i+1]*(i+1)%MOD;
    	}
    }
    long long C(int n,int m)
    {
    	if(n<m)
    	{
    		return 0;
    	}
    	return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
    }
    int n,m,k,s;
    struct Point
    {
    	int x,y;
    }point[MAXN];
    bool Cmp(Point a,Point b)
    {
    	if(a.x==b.x)
    	{
    		return a.y<b.y;
    	}
    	return a.x<b.x;
    }
    long long f[MAXN][23];
    int arr[23];
    int main()
    {
    	scanf("%d%d%d%d",&n,&m,&k,&s);
    	PreCom();//预处理组合数
    	point[0].x=1;
    	point[0].y=1;
    	REP(i,1,k)
    	{
    		scanf("%d%d",&point[i].x,&point[i].y);
    	}
    	point[k+1].x=n;
    	point[k+1].y=m;
    	sort(point+1,point+1+k,Cmp);//按 x 排序
    	f[0][0]=1;
    	REP(i,1,k+1)
    	{
    		REP(j,0,i-1)
    		{
    			if(point[j].x<=point[i].x&&point[j].y<=point[i].y)//找到可以转移过来的点
    			{
    				int len=point[i].x-point[j].x+1;
    				int wide=point[i].y-point[j].y+1;
    				long long c=C(len+wide-2,len-1);//计算两点间的路径条数
    				REP(p,1,22)
    				{
    					f[i][p]+=f[j][p-1]*c%MOD;//dp 至少经过 p 个特殊点的方案数
    					f[i][p]%=MOD;
    				}
    			}
    		}
    		REP(p,1,22)
    		{
    			f[i][p]-=f[i][p+1];//将至少经过 p 个特殊点的方案数变为恰好经过 p 个特殊点的方案数
    			f[i][p]+=MOD;
    			f[i][p]%=MOD;
    		}
    	}
    	arr[1]=s;
    	REP(i,2,21)
    	{
    		arr[i]=(arr[i-1]+1)/2;
    	}
    	long long all=C(n+m-2,n-1);
    	long long answer=0;
    	REP(i,1,21)//计算经过小于等于 log 次的方案数的贡献
    	{
    		answer+=f[k+1][i]*arr[i];
    		all-=f[k+1][i];
    		all%=MOD;
    		all+=MOD;
    		all%=MOD;
    		answer%=MOD;
    	}
    	answer+=all;
    	answer%=MOD;
    	printf("%lld
    ",answer*INV(C(n+m-2,n-1))%MOD);//计算期望并输出
    	return 0;
    }
    
  • 相关阅读:
    Silverlight:拖动滚动条控制媒体播放
    黑马程序员第一阶段3.面向对象第5天
    黑马程序员第一阶段2.java编程基础第4天
    黑马程序员第一阶段2.java编程基础第2天
    黑马程序员第一阶段2.java编程基础第3天
    黑马程序员第一阶段3.面向对象第6天
    黑马程序员第一阶段3.面向对象第8天
    黑马程序员第一阶段3.面向对象第7天
    黑马程序员第一阶段1.Java开发前奏第1天
    hdu 1133
  • 原文地址:https://www.cnblogs.com/Sxy_Limit/p/13367949.html
Copyright © 2011-2022 走看看