Description
一段长度为(m)的序列(a),按顺序分为(n)块,每块大小为(k)。
可以删除一些数,要求删除后 (mgeq n*k)。
再给定一个长度为(s)的序列(b),问是否能通过删除一些数(或不删),
使得至少一块中每个数出现的次数都不小于(b)中出现的次数。
Solution
哈又是一道用劣法过掉的题目。
首先只需要有一块满足条件即可。
假设以(i)为某一块的开头,那么需要删除((i-1))%(k)个数(因为按顺序分块),
所有(i)最远能取到的数(Next[i])就是(i+k-1+n*k-m-((i-1))%(k)),
也就是说我们可以从((i,Next[i]))中任选(k)个数组成一块。
那么将((i,Next[i]))当作一个询问,莫队搞一搞就好啦。
时间复杂度(O(msqrt{m}))。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define N 500000
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define F(x) ((x + 1) / sq)
void read(int &x) {
char ch = getchar(); x = 0;
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + ch - 48, ch = getchar();
}
struct Ques { int l, r; } q[N + 1];
int a[N + 1], b[N + 1], d[N + 1], h[N + 1];
int n, m, k, s, cnt = 0, tot = 0, sq;
bool Cmp(Ques a, Ques b) { return F(a.l) < F(b.l) || (F(a.l) == F(b.l) && ((F(a.l) & 1) ? a.r < b.r : a.r > b.r)); }
void Add(int u) { if (++ h[a[u]] == d[a[u]]) -- cnt; }
void Del(int u) { if (h[a[u]] == d[a[u]]) ++ cnt; -- h[a[u]]; }
int main() {
freopen("diana.in", "r", stdin);
freopen("diana.out", "w", stdout);
read(m), read(k), read(n), read(s);
fo(i, 1, m) read(a[i]);
fo(i, 1, s) read(b[i]);
fo(i, 1, s) if (++ d[b[i]] == 1) ++ cnt;
fo(i, 1, m - k + 1) if (m - n * k - (i - 1) % k >= 0)
q[ ++ tot ] = (Ques) { i, i + k - 1 + m - n * k - (i - 1) % k };
sq = sqrt(tot);
sort(q + 1, q + 1 + tot, Cmp);
for(int l = 1, r = 0, i = 1; i <= tot; i ++) {
while (r < q[i].r) Add(++ r);
while (l > q[i].l) Add(-- l);
while (r > q[i].r) Del(r --);
while (l < q[i].l) Del(l ++);
if (! cnt) {
printf("%d
", m - n * k);
fo(j, 1, (q[i].l - 1) % k) printf("%d ", j);
int sum = 0;
fo(j, q[i].l, q[i].r)
if (h[a[j]] > d[a[j]] && ++ sum <= m - n * k - (q[i].l - 1) % k)
printf("%d ", j), -- h[a[j]];
return 0;
}
}
puts("-1");
return 0;
}