题意:
有n*m的矩阵,然后你有k发子弹。现在你可以朝着任意列发射子弹,每一发子弹都会使该列上的数值-1,最小减少到0。
现在问你连续最长的行数,在k发子弹内,使得这些行上的数值全部为0.
思路:
简单的二分枚举最长行数区间,每个区间的最大值决定了要发射的子弹数,所以是RMQ问题,当然这里的枚举全部枚举,用尺取法也可以。
//889 ms #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #define M 100005 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define root 1,n,1 using namespace std; int n,m,lim; int Max[6][M<<2]; int shots[6]; void pushup(int rt) { for(int i=1;i<=m;i++){ Max[i][rt]=max(Max[i][rt<<1],Max[i][rt<<1|1]); } } void build(int l,int r,int rt) { if(l==r){ for(int i=1;i<=m;i++){ scanf("%d",&Max[i][rt]); } return ; } int m=(l+r)>>1; build(lson); build(rson); pushup(rt); } int query(int*Max,int L,int R,int l,int r,int rt) { if(L<=l&&r<=R){ return Max[rt]; } int m=(l+r)>>1; int q1=-1,q2=-1; if(L<=m) q1=query(Max,L,R,lson); if(R>m) q2=query(Max,L,R,rson); return max(q1,q2); } bool ok(int x) { int cnt=0; for(int j=1;j+x-1<=n;j++){ cnt=0; for(int k=1;k<=m;k++){ int p=query(Max[k],j,j+x-1,root); cnt+=p; } if(cnt<=lim) return true; } return false; } int main() { scanf("%d%d%d",&n,&m,&lim); build(root); int lb=0,ub=n+1; while(ub-lb>1){ //二分出最大区间长度 int mid=(ub+lb)>>1; if(ok(mid)){ lb=mid; } else ub=mid; } for(int j=1;j+lb-1<=n&&lb;j++){ //当lb=0的时候说明一个机器人也破坏不了,这个时候不能再查询,否则会RE int cnt=0; for(int k=1;k<=m;k++){ int p=query(Max[k],j,j+lb-1,root); cnt+=p; shots[k]=p; } if(cnt<=lim)break; } printf("%d",shots[1]); for(int i=2;i<=m;i++){ printf(" %d",shots[i]); } puts(""); return 0; }
Codeforces #291 (Div. 2) D. R2D2 and Droid Army(RMQ+二分)
原文:http://blog.csdn.net/kalilili/article/details/43882911