听说这题用双端队列做,不过某渣连双端队列都没听说过。。。囧
最后看到别人博客说用前缀和可以解决,虽然是险过,但是我觉得这个好吊的样子。
链接:
http://blog.csdn.net/yangjie_acm/article/details/24433573
首先,用一个pos数组记录第i个1的位置,用sum数组记录前i个1的位置的和
从第一个1的位置,连续的1的个数最大值为1开始枚举, 如果当前位置和最大值可行,更新ans= max,max++, 否则max不变 推后到下一个1的位置
判断当前位置和最大值可行的办法:
计算左边的1右边的1 的移动次数之和是否大于最大值K即可
例如:
100101010101 , 枚举到第2个1的位置, max= 5时, (设be= 2) 最中间的1 为 第mid= (be + be+ max -1) /2 = 4 个1, 左边1的个数为num= mid- be个
左边的移动次数 costl = pos【4】 - pos【3】 + pos【4】 + pos【2】 - num*(num+1) /2;
上式中num*(num+1)/2 是因为第i个1移动的次数都要减去i
式子化简可得 costl= pos【4】*2 - sum【mid-1】 -sum【be-1】 - num*(num+1)/2;
右边1的个数numr= max -num -1 ;
同理可得右边的移动次数 costr= sum【be+max-1】 - sum【mid】 - pos【4】*2 - numr*(numr+1)/2;
代码:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <time.h>
using namespace std;
#define maxn 100000
long long sum[maxn+5];
int pos[maxn+5];
char ch[maxn+5];
long long k;
bool ok(int l, int r)
{
int mid= (l + r)/2;
int num= mid - l; //左边1的个数 ;
long long temp= pos[mid]* num - num*(num + 1)/2 - ( sum[mid-1] - sum[l-1] );
num= r- mid;
temp+= sum[r] - sum[mid] - num *pos[mid] - num *(num+1)/2;
if(temp > k)
return false;
else
return true;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(pos, 0, sizeof(pos));
scanf("%s %lld",ch+1, &k);
int len= strlen(ch+1);
int count= 0;
sum[0]= 0;
for(int i= 1; i<= len; i++)
{
if(ch[i]=='1')
{
count++;
pos[count]= i;
sum[count]= sum[count-1]+pos[count];
}
}
int max= 1;
int be= 1;
int ans= 0;
while(true)
{
if(max+ be -1>count)
break;
if( ok(be, be+max-1))
{
ans= max;
max++;
}
else
be++;
}
printf("%d\n",ans);
}
return 0;
}