这道题很巧妙,根据数据,初步分析复杂度小于等于nlogn
先给一个解题报告:https://wenku.baidu.com/view/b97cd22d0066f5335a8121a3.html
边界问题
首先注意边界问题:有多个解,优先短,然后优先起点小。
优先短:while (q.size() > 1)循环里面的calk判断必须取等号
优先起点小:由于是从前往后遍历,自然起点小的值先存进去,不用特意判断。
题意:
思路
关于上凸点可忽略的证明:
最后在下凸点中找到最优解:
#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
#include<math.h>
#include<string>
#include<string.h>
#include<map>
#include<unordered_map>
#include<unordered_set>
#include<set>
#include<stack>
#include<sstream>
using namespace std;
int sum[100005], a[100005], p[100005], ans[2];
int calk(int a, int b, int c, int d) {
return (sum[a] - sum[b])*(c - d) - (sum[c] - sum[d])*(a - b);
}
int main()
{
int t;
cin >> t;
while (t--) {
int n, L;
cin >> n >> L;
string s;
cin >> s;
sum[0] = 0;
for (int i = 1; i <= n; i++)
sum[i] = s[i - 1] - '0' + sum[i - 1];
deque<int> q;
ans[0] = 0, ans[1] = L;
for (int i = L; i <= n; i++)
{
int j = i - L;
while (q.size() >= 2) {
int a = q[q.size() - 1];
int b = q[q.size() - 2];
if (calk(j, a, a, b) <= 0) //等号可取可不取
q.pop_back();
else
break;
}
q.push_back(j);
while (q.size() > 1) {
int a = q[0];
int b = q[1];
if (calk(i, b, i, a) >= 0)//等号必取
q.pop_front();
else
break;
}
if (calk(i, q.front(), ans[1], ans[0]) > 0 || (calk(i, q.front(), ans[1], ans[0]) == 0 && i - q.front() < ans[1] - ans[0]))
{
ans[0] = q.front();
ans[1] = i;
}
}
cout << ans[0] + 1 << " " << ans[1] << endl;
}
return 0;
}