题目大意:
一共有N个人,每个人有M个属性值,当一个人的所有属性值都小于等于0的时候,这个人就算被销毁了。
我们每次操作可以选一种属性值进行攻击,使得所有人的这个属性的值都-1.
我们最多可以进行K次操作,
问我们最多可以干掉多少个连续的人。
问这种时候的具体操作(每一种属性用了多少次操作)。
思路:
1、比较经典的模型,对于连续的X个人,假如都将其干掉的时候,需要对于每种属性使用的最少操作,就是对应这连续的X个人每种属性的最大值。
2、那么问题转化到区间最大值上来,这里我们可以使用RMQ来解,也可以用线段树来解。
接下来我们可以考虑枚举人数,然后O(NLogN)的去维护当前情况是否可行,直到枚举到不可行为止前的那个答案,就是最终答案。
由此看来,枚举人数是具有单调性的,要干掉更多的人,就需要更多的操作,那么我们可以二分这个人数。
对于可行方案,增加人数,不可行方案,减少人数。
3、二分过程中,维护最后一次可行解的答案,输出即可。
Ac代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
using namespace std;
int maxn[10][200005][20];
int a[200060][10];
int output[10];
int n,m,k;
void ST()
{
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
maxn[i][j][0]=a[j][i];
}
}
int len=floor(log10(double(n))/log10(double(2)));
for(int z=1;z<=m;z++)
{
for(int j=1;j<=len;j++)
{
for(int i=1;i<=n+1-(1<<j);i++)
{
maxn[z][i][j]=max(maxn[z][i][j-1],maxn[z][i+(1<<(j-1))][j-1]);
}
}
}
}
int Slove(int mid)
{
int ans[10];
for(int i=1;i<=n;i++)
{
if(i+mid-1>n)break;
else
{
int a=i;int b=i+mid-1;
int len= floor(log10(double(b-a+1))/log10(double(2)));
for(int z=1;z<=m;z++)
{
ans[z]=max(maxn[z][a][len], maxn[z][b-(1<<len)+1][len]);
}
int sum=0;
for(int z=1;z<=m;z++)
{
sum+=ans[z];
}
if(sum<=k)
{
for(int z=1;z<=m;z++)
{
output[z]=ans[z];
}
return 1;
}
}
}
return 0;
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&k))
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
}
}
ST();
int l=0;
int r=n;
while(r-l>=0)
{
int mid=(l+r)/2;
if(Slove(mid)==1)
{
l=mid+1;
}
else r=mid-1;
}
for(int i=1;i<=m;i++)
{
printf("%d ",output[i]);
}
printf("\n");
}
}