P3120 [USACO15FEB]牛跳房子(线段树优化$dp$($CDQ$分治))
很容易想到朴素方程,令$f[i][j]$表示到$i$行,$j$列的方案数
有
$$f[i][j] = \sum f[k][l]\;(k<i,l<j,a[k][l] \neq a[i][j])$$
但显然是$O(n^4)$的,其实这看起来就想二维偏序,但实际它有三个条件,可以$CDQ$分治(但我不会)
考虑吧方程换个形式,令$sum = \sum f[k][l]\;(k<i,l<j)$,$x = \sum f[k][l]\;(k<i,l<j,a[k][l] = a[i][j])$
有
$$f[i][j] = sum - x$$
(类似那道染色的思想)
可以一行一行地推(就能不用二维线段树,只用一维),令$sum[i]$表示$(1,1)$到上一行$i$列的矩形区域的前缀和(原本是二维,现在只用一维)
对于每种标记开一颗线段树,即开$k$颗线段树,这时便需要动态开点
时间复杂度 $O(n*m*\log m)$
空间复杂度 $O(k*\log m)$
代码如下
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 #include <cstdio> 5 #define ll long long 6 using namespace std; 7 8 template <typename T> void in(T &x) { 9 x = 0; T f = 1; char ch = getchar(); 10 while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();} 11 while( isdigit(ch)) {x = 10 * x + ch - 48; ch = getchar();} 12 x *= f; 13 } 14 15 template <typename T> void out(T x) { 16 if(x < 0) x = -x , putchar('-'); 17 if(x > 9) out(x/10); 18 putchar(x%10 + 48); 19 } 20 //------------------------------------------------------- 21 22 const int N = 800,mod = 1e9+7; 23 24 int n,m,k; 25 int a[N][N];ll f[N][N],sum[N]; 26 int cnt,rt[N*N]; 27 struct node { 28 int lc,rc; ll sum; 29 }t[6000000]; 30 31 void A(int &u,int l,int r,int pos,int add) { 32 if(!u) u = ++cnt; 33 if(l == r) {t[u].sum = (t[u].sum+add)%mod; return;} 34 int mid = (l+r)>>1; 35 if(pos <= mid) A(t[u].lc,l,mid,pos,add); 36 else A(t[u].rc,mid+1,r,pos,add); 37 t[u].sum = (t[t[u].lc].sum + t[t[u].rc].sum)%mod; 38 } 39 40 ll Q(int u,int l,int r,int a,int b) { 41 if(!u) return 0; 42 if(a <= l && b >= r) return t[u].sum; 43 int mid = (l+r)>>1,res = 0; 44 if(a <= mid) res = (res + Q(t[u].lc,l,mid,a,b))%mod; 45 if(b > mid) res = (res + Q(t[u].rc,mid+1,r,a,b))%mod; 46 return res; 47 } 48 49 int main() { 50 freopen("0.in","r",stdin); 51 int i,j; 52 in(n); in(m); in(k);// 53 for(i = 1;i <= n; ++i) for(j = 1;j <= m; ++j) in(a[i][j]);// 54 f[1][1] = 1; A(rt[a[1][1]],1,m,1,1); 55 //sum[1] = 1; 56 for(i = 1;i <= m; ++i) sum[i] = 1;// 57 for(i = 2;i <= n; ++i) { 58 for(j = 2;j <= m; ++j) { 59 f[i][j] = (sum[j-1] - Q(rt[a[i][j]],1,m,1,j-1))%mod; 60 //A(rt[a[i][j]],1,m,j,f[i][j]);//debug 当前的线段树保存的是上一行的信息,要推完这一行才能更新 61 } 62 ll tmp = 0; 63 for(j = 2;j <= m; ++j) { 64 tmp = (tmp + f[i][j])%mod; 65 sum[j] = (sum[j] + tmp)%mod; 66 A(rt[a[i][j]],1,m,j,f[i][j]);//right 67 } 68 } 69 out((f[n][m]+mod)%mod); 70 return 0; 71 }