cf1120 A. Diana and Liana(思维,定长的双指针)
题意:
把长为m的数组分为n个长为k的区间 \([a_1,a_{k}],[a_{1+k},a_{2k}],\cdots , [a_{1+(n-1)k},a_{nk}]\) ,多出来的 \([a_{nk},a_m]\) 不用管。题目保证一定够分。现在可以删除若干个数,使得至少有一个区间中,\(b_i\) 至少出现一次,且总区间数仍为n。b数组可能有重复值,表示某个数要出现至少多次。求删数方案。
思路:
\(O(n)\) 这题思路很简单,比那种不定长的双指针简单多了。但我码力不行,写得很慢。
最多可以删掉 \(m-kn\) 个数,所以可以把原数组分为 \(n-1\) 个长为 \(k\) 的区间和 \(1\) 个长为 \(len=k+(m-kn)\) 的区间。
接下来只需看是否存在长为 \(len\) 的合法区间。开桶记录区间中每个数出现的次数 have[a[i]],双指针更新左右端点和桶,并与每个数需要的数量 need[a[i]] 比较。注意不是所有的长为 \(len\) 的区间都能取,左端点必须为 \(1+ik\) 的形式。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
int n, m, k, s, a[N];
int need[N], have[N], types;
void output(int l, int r)
{
vector<int> ans; int len = r - l + 1;
for(int i = l; len > k && i <= r; i++)
if(have[a[i]] > need[a[i]]) ans.push_back(i), have[a[i]]--, len--;
printf("%d\n", ans.size());
for(int i : ans) printf("%d ", i);
}
signed main()
{
scanf("%d%d%d%d", &m, &k, &n, &s);
for(int i = 1; i <= m; i++) scanf("%d", &a[i]);
for(int i = 1; i <= s; i++)
{
int x; scanf("%d", &x);
if(++need[x] == 1) types++;
}
int len = k + m - k * n, typecnt = 0;
for(int i = 1; i <= len; i++)
if(++have[a[i]] == need[a[i]]) typecnt++;
for(int l = 1, r = len; r <= m; l += 1, r = l + len - 1)
{
if((l-1)%k == 0 && typecnt == types) return output(l, r), 0;
if(r == m) return puts("-1"), 0;
if(++have[a[r+1]] == need[a[r+1]]) typecnt++;
if(--have[a[l]] == need[a[l]] - 1) typecnt--;
}
return 0;
}

浙公网安备 33010602011771号