【bzoj3939】[Usaco2015 Feb]Cow Hopscotch 动态开点线段树优化dp
题目描述
Just like humans enjoy playing the game of Hopscotch, Farmer John's cows have invented a variant of the game for themselves to play. Being played by clumsy animals weighing nearly a ton, Cow Hopscotch almost always ends in disaster, but this has surprisingly not deterred the cows from attempting to play nearly every afternoon.
The game is played on an R by C grid (2 <= R <= 750, 2 <= C <= 750), where each square is labeled with an integer in the range 1..K (1 <= K <= R*C). Cows start in the top-left square and move to the bottom-right square by a sequence of jumps, where a jump is valid if and only if
1) You are jumping to a square labeled with a different integer than your current square,
2) The square that you are jumping to is at least one row below the current square that you are on, and
3) The square that you are jumping to is at least one column to the right of the current square that you are on.
Please help the cows compute the number of different possible sequences of valid jumps that will take them from the top-left square to the bottom-right square.
输入
输出
Output the number of different ways one can jump from the top-left square to the bottom-right square, mod 1000000007.
样例输入
4 4 4
1 1 1 1
1 3 2 1
1 2 4 1
1 1 1 1
样例输出
5
题解
动态开点线段树优化dp
首先有dp方程$f[x][y]=\sum\limits_{i=1}^{x-1}\sum\limits_{j=1\& c[i][j]\neq c[x][y]}^{y-1}f[i][j]=\sum\limits_{i=1}^{x-1}\sum\limits_{j=1}^{y-1}f[i][j]-\sum\limits_{i=1}^{x-1}\sum\limits_{j=1\& c[i][j]=c[x][y]}^{y-1}f[i][j]$。
前面那个东西我们可以使用二维前缀和优化,而后面的那个东西只能使用数据结构。
考虑我们的dp方式:先循环行、再循环列。那么搜到某一行时,它之前的一定是行数比它小的,所以不用管这个条件,只要维护列即可,即维护第1~x列某种颜色的f之和。
由于颜色数太多,显然不能使用静态数据结构,所以使用动态开点线段树,对每一个颜色开一棵线段树维护区间和。
对于第i行,先对于第j列求出1~j-1列与它颜色相同的f的和,然后再循环一遍,更新二维前缀和及线段树。
时间复杂度$O(nm\log nm)$
#include <cstdio> #include <algorithm> #define N 800 #define M 600000 using namespace std; const int mod = 1000000007; int c[N][N] , f[N][N] , sum[N][N] , root[M] , ls[M * 15] , rs[M * 15] , si[M * 15] , tot; void update(int p , int a , int l , int r , int &x) { if(!x) x = ++tot; si[x] = (si[x] + a) % mod; if(l == r) return; int mid = (l + r) >> 1; if(p <= mid) update(p , a , l , mid , ls[x]); else update(p , a , mid + 1 , r , rs[x]); } int query(int b , int e , int l , int r , int x) { if(b <= l && r <= e) return si[x]; int mid = (l + r) >> 1 , ans = 0; if(b <= mid) ans += query(b , e , l , mid , ls[x]); if(e > mid) ans += query(b , e , mid + 1 , r , rs[x]); return ans % mod; } int main() { int n , m , i , j; scanf("%d%d%*d" , &n , &m); for(i = 1 ; i <= n ; i ++ ) for(j = 1 ; j <= m ; j ++ ) scanf("%d" , &c[i][j]); f[1][1] = sum[1][1] = 1 , update(1 , 1 , 1 , m , root[c[1][1]]); for(i = 2 ; i <= n ; i ++ ) sum[i][1] = 1; for(i = 2 ; i <= m ; i ++ ) sum[1][i] = 1; for(i = 2 ; i <= n ; i ++ ) { for(j = 2 ; j <= m ; j ++ ) f[i][j] = (sum[i - 1][j - 1] - query(1 , j - 1 , 1 , m , root[c[i][j]]) + mod) % mod; for(j = 2 ; j <= m ; j ++ ) sum[i][j] = (((sum[i][j - 1] + sum[i - 1][j]) % mod + f[i][j]) % mod - sum[i - 1][j - 1] + mod) % mod; for(j = 2 ; j <= m ; j ++ ) update(j , f[i][j] , 1 , m , root[c[i][j]]); } printf("%d\n" , f[n][m]); return 0; }