题目描述
难度分:1300
输入T(≤103)表示T组数据。所有数据的n之和≤2×105。
每组数据输入n(1≤n≤2×105),k(1≤k≤109)和长为n的数组 a(1≤a[i]≤104),长为n的数组h(1≤h[i]≤109)。
选一个子区间[L,R],对于[L,R−1]内的i,满足h[i]是h[i+1]的倍数,且a[L]+a[L+1]+…+a[R]不超过k。
输出子区间长度R−L+1的最大值。如果没有这样的子区间,输出0。
输入样例
5
5 12
3 2 4 1 8
4 4 2 4 1
4 8
5 4 1 2
6 2 3 1
3 12
7 9 10
2 2 4
1 10
11
1
7 10
2 6 3 1 5 10 6
72 24 24 12 4 4 2
输出样例
3
2
1
0
3
算法
动态规划
先做一个简单的线性DP
,相当于预处理。
状态定义
dp[i]表示以i位置开头且满足h[i]|h[i+1](|为整除符号,a|b表示b能被a整除),能够往右延伸的最大长度。
状态转移
如果h[i]|h[i+1],就进行状态转移dp[i]=dp[i+1]+1,否则dp[i]=1。
二分
考虑到a[i]≥1,所以对于某个固定的子数组左端点l,子数组一定是越长和越大,这就有了单调性。因此遍历l∈[1,n],二分找到满足Σri=la[i]≤k的最远r,对每个l维护r−l+1的最大值即可得到最终答案。这时候上面的预处理就有了作用,二分的范围就是r∈[l,l+dp[l]−1]。
复杂度分析
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int T, n, k, a[N], h[N], s[N], dp[N];
void solve() {
dp[n] = 1;
for(int i = n - 1; i >= 1; i--) {
if(h[i] % h[i + 1] == 0) {
dp[i] = dp[i + 1] + 1;
}else {
dp[i] = 1;
}
}
int ans = 0;
for(int l = 1; l <= n; l++) {
int r = l + dp[l] - 1;
int low = l, high = r;
while(low < high) {
int mid = low + high + 1 >> 1;
if(s[mid] - s[l - 1] <= k) {
low = mid;
}else {
high = mid - 1;
}
}
if(s[high] - s[l - 1] <= k) {
ans = max(ans, high - l + 1);
}
}
printf("%d\n", ans);
}
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
s[i] = s[i - 1] + a[i];
}
for(int i = 1; i <= n; i++) {
scanf("%d", &h[i]);
}
solve();
}
return 0;
}