洛谷P3120 [USACO15FEB]Cow Hopscotch G(动态开点线段树优化dp/cdq分治)
题目大意:
就像人类喜欢跳格子游戏一样,FJ的奶牛们发明了一种新的跳格子游戏。虽然这种接近一吨的笨拙的动物玩跳格子游戏几乎总是不愉快地结束,但是这并没有阻止奶牛们在每天下午参加跳格子游戏
游戏在一个R*C的网格上进行,每个格子有一个取值在1-k之间的整数标号,奶牛开始在左上角的格子,目的是通过若干次跳跃后到达右下角的格子,当且仅当格子A和格子B满足如下条件时能从格子A跳到格子B:
1.B格子在A格子的严格右方(B的列号严格大于A的列号)
2.B格子在A格子的严格下方(B的行号严格大于A的行号)
3.B格子的标号和A格子的标号不同
请你帮助奶牛计算出从左上角的格子到右下角的格子一共有多少种不同的方案
题目思路:
这个题鸭,一眼前缀和优化dp啊
内存限制(128MB)--solution1:
对于一个格子(i,j) 能转移过来的点是(i,j)左上角的所有的点
那我们用一个sum数组记录转移到(i,j)这个点左侧且上侧所有点的方案数的和
那么转移方程就比较明显了
dp[i[[j] = sum[i-1][j-1] - sum(color[i][j]) 在所有点中和(i,j)这个点颜色相同的点的
但是这里我们如果给颜色再开一维k的话,不仅时间复杂度达到O(nmk)空间复杂度也是无法接受的
所以这里我们用线段树优化这一维
我们给每种颜色开一颗线段树
因为格子是750×750的,要是每种颜色都要出现的话,那么线段树也不用开的很大,所以这里选择用动态开点
我们每次在第i行更新dp[i]的时候,保证用的都是上一层的状态
然后更新完dp数组在更新线段树
int ans, n,m,k,c[766][766],indexx; ll sum[777][777],dp[777][777]; struct node { int ls,rs,s; } a[755*755*10]; void push(int st) { a[st].s = (a[a[st].ls].s+a[a[st].rs].s)%mod; } void update(int &st,int l,int r,int pos , int num) { if(!st) st= ++indexx; if(l==r) { a[st].s= (a[st].s + num)%mod; return ; } int mid = (l+r)>>1; if(pos<=mid) update(a[st].ls,l,mid,pos,num); else update(a[st].rs,mid+1,r,pos,num); push(st); } int query(int st,int l,int r,int ql,int qr) { if(!st) return 0; if(ql<=l&&qr>=r) return a[st].s; int mid = (l+r)>>1; ll ans=0; if(ql<=mid) ans=( ans + query(a[st].ls,l,mid,ql,qr) ) %mod; if(qr>mid) ans = ( ans +query(a[st].rs,mid+1,r,ql,qr) ) %mod; return ans; } int main() { n=read(),m=read(),k=read(); rep(i,1,n) rep(j,1,m) c[i][j] = read(),sum[1][j] = 1; indexx = k; dp[1][1]=1; update(c[1][1],1,m,1,1); for(int i=2 ; i<=n ; i++) { for(int j=2 ; j<=m ; j++) dp[i][j]=(sum[i-1][j-1] - query(c[i][j],1,m,1,j-1) + mod )%mod; ll res=0; for(int j=1 ; j<=m ; j++) { res=(res+dp[i][j])%mod; sum[i][j] = (sum[i-1][j]+res)%mod; update(c[i][j],1,m,j,dp[i][j]); } } out(dp[n][m]); return 0; }
内存限制(64MB)--solution2:
CDQ分治
不会,没看懂,待补-----