题意:
有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;
}