【cf1247E】E. Rock Is Push(dp+二分)

传送门

题意:
现有大小为\(n\cdot m,n,m\leq 2000\)的网格,上面有些箱子。
你位于\((1,1)\)要走到\((n,m)\),每步只能向右或者向下走,并且在走的过程中遇到箱子能够推动箱子。注意箱子不能重在一起或者超出这个边界。
问一共有多少种走法。

思路:
最常规的\(dp\)思路,直接定义\(dp_{i,j}\)为走到\((i,j)\)格子的方案数。但是我们会发现这样定义无法转移,因为我们不知道箱子的状态,如果加上箱子的状态空间复杂度和时间复杂度直接爆炸。
这里我们考虑细化\(dp\)的状态,定义\(dp_{i,j,0/1}\)表示走到\((i,j)\)\(0\)是从左边走过来,\(1\)是从上边走过来的方案数。
这样我们对于每个格子分别两种情况转移,以从上边走过来为例,我们只需要考虑其上面的位置从左边走过来的所有情况即可。最终转移过来的必定会是连续的一段区间,二分一些找到区间端点,我们就能知道合法的状态转移位置。通过维护列上面的前缀和直接转移即可。
细节可能有点多,详见代码:

/*
 * Author:  heyuhhh
 * Created Time:  2020/3/6 16:56:08
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2000 + 5, MOD = 1e9 + 7;

int n, m;
char s[N][N];
int col[N][N], row[N][N];
int dp[N][N][2];
int sumc[N][N], sumr[N][N];

void add(int &x, int y) {
    if(y < 0) y += MOD;
    if(y >= MOD) y -= MOD;
    x += y;
    if(x >= MOD) x -= MOD;   
}

void run() {
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> (s[i] + 1);
    for(int i = 1; i <= n; i++) {
        for(int j = m; j >= 1; j--) {
            row[i][j] = row[i][j + 1] + (s[i][j] == 'R');
        }
        sort(row[i] + 1, row[i] + m + 1);
    }
    for(int j = 1; j <= m; j++) {
        for(int i = n; i >= 1; i--) {
            col[j][i] = col[j][i + 1] + (s[i][j] == 'R');
        }
        sort(col[j] + 1, col[j] + n + 1);
    }
    dp[1][1][0] = 1;
    sumr[1][1] = sumc[1][1] = 1;
    for(int j = 2; j <= m - row[1][m]; j++) {
        dp[1][j][0] = 1;
        sumc[1][j] = 1;
    }
    for(int i = 2; i <= n - col[1][n]; i++) {
        dp[i][1][1] = 1;
        sumr[i][1] = 1;
    }
    for(int i = 2; i <= n; i++) {
        for(int j = 2; j <= m; j++) {
            int p = lower_bound(col[j] + n - i + 2, col[j] + n + 1, n - i + 1) - col[j];
            p = n - p + 1;
            if(col[j][n - i + 1] != n - i + 1) add(dp[i][j][1], sumc[i - 1][j] - sumc[max(0, p - 1)][j]);
            p = lower_bound(row[i] + m - j + 2, row[i] + m + 1, m - j + 1) - row[i];
            p = m - p + 1;
            if(row[i][m - j + 1] != m - j + 1) add(dp[i][j][0], sumr[i][j - 1] - sumr[i][max(0, p - 1)]);
            add(sumr[i][j], sumr[i][j - 1] + dp[i][j][1]);
            add(sumc[i][j], sumc[i - 1][j] + dp[i][j][0]);
        }
    }
    int ans = (dp[n][m][1] + dp[n][m][0]) % MOD;
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}
posted @ 2020-03-09 22:00  heyuhhh  阅读(162)  评论(0编辑  收藏  举报