BZOJ1499: 瑰丽华尔兹(单调队列)
pro: 给出一个n*m的地图,刚开始人在(x,y),每次给出一段区间(l,r,t),表示在时间[l,r]内,可以使人向4个方向(t)移动一格,或者不动。求最大可以移动多少格。
sol: 考虑每一列(上下移)或者一行(左右移)的情况。以右移为例,我们可以很快列出dp方程:f[x][y][i]=max(f[x][y][i],f[x][j][i]+y-j)。这个dp方程我们可以用单调队列维护,所以复杂度就是nmk的。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=210; const int inf=1e9+7; int dp[maxn][maxn][2],ans; char c[maxn][maxn]; int X[5]={0,-1,1,0,0},Y[5]={0,0,0,-1,1}; int q[maxn][3],head,tail,N,M,now; int update(int x,int y,int tp,int tk) { int res=dp[q[tp][0]][q[tp][1]][(tk^1)&1]+now-q[tp][2]; dp[x][y][tk&1]=max(dp[x][y][tk&1],res); return res; } int get(int tp,int tk) { int res=dp[q[tp][0]][q[tp][1]][(tk^1)&1]+now-q[tp][2]; return res; } void work(int x,int y,int tK,int len,int opt) { head=1,tail=0; now=0; while(x>=1&&x<=N&&y>=1&&y<=M) { dp[x][y][tK&1]=dp[x][y][(tK^1)&1]; if(c[x][y]=='x') head=1,tail=0; while(tail>=head&&now-q[head][2]>len) head++; while(tail>=head&&get(tail,tK)<dp[x][y][(tK^1)&1]) tail--; if(tail>=head) update(x,y,head,tK); ans=max(ans,dp[x][y][tK&1]); tail++; q[tail][0]=x; q[tail][1]=y; q[tail][2]=now++; x+=X[opt]; y+=Y[opt]; } } int main() { int K,x,y,L,R,opt; scanf("%d%d%d%d%d",&N,&M,&x,&y,&K); rep(i,1,N) scanf("%s",c[i]+1); rep(i,1,N) rep(j,1,M) rep(k,0,K) dp[i][j][k]=-inf; dp[x][y][0]=0; rep(i,1,K){ scanf("%d%d%d",&L,&R,&opt); if(opt==1) rep(j,1,M) work(N,j,i,R-L+1,opt); if(opt==2) rep(j,1,M) work(1,j,i,R-L+1,opt); if(opt==3) rep(j,1,N) work(j,M,i,R-L+1,opt); if(opt==4) rep(j,1,N) work(j,1,i,R-L+1,opt); } printf("%d\n",ans); return 0; }
It is your time to fight!