CodeForces - 1120A(Diana and Liana)

题意:给 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值