题意:给 n 朵花,每连续的 k 朵 可以组成一个花环,要求组成 m 个花环,但其中一个花环要包含给定的 s 朵花(给出s朵花的种类,顺序不做要求),也就是说,其中一个连续 k 朵花组成的花环中必须有那 s 种花。求出最大可以删除的花朵数量,并输出这些花的坐标,答案不唯一,输出任意一个。
分析:题目只对一只花环做特殊要求,其它的只要是连续的 k 朵花就行。所以我们需要找到一个合理的区间[l,r],满足这个区间包含需要的 s 朵花,那么这个区间最大长度 len 是多少,m 个花环要消耗 m*k 朵花,所以最多删除 n-m*k 朵花,而且如果有解,也一定可以删除这么多朵。所以 len = n-m*k+k,即最多删除的花朵数+一只花环需要 k 朵。我们可以把区间长度固定在 len(因为一定有解)这样我们在这个区间删除 n-m*k 朵多余的花 就是答案了。怎么写,从开头维护一个长度为 len 的区间[l,r],往后滑动,每次记录滑动的影响就OK了([l,r]->[l+1,r+1],把l,r+1的影响更新一下),还有一点,其它花环没有特殊要求,但也必须是连续的 k 个,所以,这个区间外左右两边必须都是 k 的倍数((l-1)% k 简单判断一下就好了)。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 5*1E5+10;
bool vis[N]={false};
int a[N];
int need[N]={0};
int cnt[N]={0};
int ans[N];
int main()
{
int n,k,m,s;
scanf("%d%d%d%d",&n,&k,&m,&s);
for(int i=1;i<=n;i++) scanf("%d", &a[i]);
int len = n-k*(m-1); //特殊区间长度上限
int del = n-k*m; //最多可以删除元素的个数
int type=0;//记录s中共有几种类型的花
for(int i=1;i<=s;i++) //特殊花环需要的组成
{
int v;
scanf("%d",&v);
if(!need[v]) type++,vis[v]=true; //s需要这种花
++need[v];
}
bool flag=false;
int L=1;
for(int i=1;i<=len;i++) //先算【1,len】这个区间,后面方便遍历
{
int v=a[i];
cnt[v]++;
if(vis[v]&&cnt[v]==need[v]) type--; //这个类型数量达标,需要的花型-1
if(type==0)
{
L=1;
flag=true;
break;
}
}
if(!flag)
for(int i=len+1;i<=n;i++) //往后遍历,区间长度不变
{
int l=a[L];
int r=a[i];
L++; //左边界平移
cnt[l]--;
if(vis[l]) //减掉左边出去的元素
{
if(cnt[l]==need[l]-1) type++;
}
cnt[r]++;
if(vis[r]) //增加右边进来的元素
{
if(cnt[r]==need[r]) type--;
}
if(type==0&&(L-1)%k==0)
{
flag=true;
break;
}
}
int num=0;
if(flag)
{
for(int i=L;i<len+L;i++)
{
int v=a[i];
if(need[v]>0)
{
need[v]--;
continue;
}
if(num==del) break;
ans[++num]=i;
}
printf("%d\n",num);
for(int i=1;i<=num;i++)
{
printf("%d%c",ans[i],i==num?'\n':' ');
}
}
else puts("-1");
return 0;
}