题目链接:http://codeforces.com/problemset/problem/722/E
题目大意:有一个n*m的网格,起点(1,1),终点(n,m),每次随机向右或向下走。一开始的能量为s,其中有k个网格异常,经过这样的网格会使能量从原来的x变为x/2的上取整(即(x+1)/2)。求最后到达(n,m)时的期望能量值。
数据范围:1 ≤ n, m ≤ 100 000, 0 ≤ k ≤ 2000, 1 ≤ s ≤ 1 000 000
题解:由于s每次遇到异常的网格都减半,最多减logs次就变成1,这样能量值最多有20种,所以我们只需要统计走到终点时每种能量值的方案数即可。
首先将点的坐标以x为第一关键字,y为第二关键字排序,然后在最前面添一个点(1,1)(即第0个点),从后往前进行dp(由于一开始想叉了写了从后往前的dp,这里也就依照代码进行分析,事实上从前往后也一样。)
为了方便,令d[i][j]表示从点 i 走到点 j 的方案数,即d[i][j]=C(p[j].x-p[i].x+p[j].y-p[i].y,p[j].x-p[i].x)。
设g[i][j]表示从点(n,m)走到第 i 个点,途中恰好经过了 j 个异常点(不包括点 i )的方案数。直接求g不太好求,那么我们再设f[i][j]表示经过了不超过j个点的方案数,这样只要在总方案数中减去超过了 j 个点的方案数即可。所以我们枚举经过的第 j 个异常点,则f[i][j]=d[i][(n,m)]-∑(g[l][j]*d[i][l]),g[i][j]=f[i][j]-f[i][j-1],其中p[i].x<=p[l].x且p[i].y<=p[l].y。
计算出所有的方案数之后,最后答案为(∑g[0][i]*a[i])/d[(1,1)][(n,m)],a[i]表示经过 i 个异常点后s的值。
时间复杂度O(n^2logs)
代码如下:
#include <algorithm>
#include <cstdio>
using namespace std;
const int mo=1000000007;
int a[30],fac[200005],ine[200005],g[20