听说这题用双端队列做,不过某渣连双端队列都没听说过。。。囧
最后看到别人博客说用前缀和可以解决,虽然是险过,但是我觉得这个好吊的样子。
链接:
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; }