Diana and Liana CodeForces - 1121D_思维

3 篇文章 0 订阅

题目链接:

Diana and Liana

CodeForces - 1121D

题意:

给你 n 朵花,连续的 k 朵可以可以组成一个花环,最后需要构成至少 m 个花环,至少有一个花环包含 s 个规定的花朵,

输入n朵花,这每个数代表花的种类。

输入s,至少有一个花环需要包含这s个花

输出 可以删除多少个花,可以使得满足给定的条件,并输出花的位置标号,从左到右,编号从 1 递增。

如果不存在,输出-1.

题解:

对于这个题,首先需要找到一个区间,对于这个区间里的至少要包含 这s朵花,那么怎么找这样的区间呢,暴力 n ^ 2 显然不行。那么怎么找这样的区间的,可以这样思考:给你一个区间范围,让你在这个区间范围里找是否满足包含这 s 朵花。那么这个区间越大越好,但是也不能特别大因为还需要留出空间组成其他的 m - 1个花环。
首先,最后你要组成 m 个花环,且每个花环是要连续的 k 朵才可以构成,那么最后最少需要剩下 m * k 朵花,所以你最多可以删除 n - m * k 朵花,所以你可以有的最大空间就是 n - m * k + k。如果连续的n - m * k + k 这个区间里还找不到满足的含有s朵规定的花,那么就只能输出 -1了,所以只需要 o(n) 的复杂度从头到尾遍历一遍就好。需要注意的是:如果一个区间满足含有s朵花的话,那么这个区间前多包含的花的数目应该是 k 的倍数,因为题目要求需要连续的k朵才可以形成花环。
还不明白可以看代码,有详细注释。

代码实现写的可能麻烦了些,按这个整体思路就是 找  n - m * k + k  这么大的区间。明白了代码实现思路,其实也不麻烦,代码多了好想,都是一点点暴力的~

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<sstream>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#define up(i, x, y) for(int i = x; i <= y; i++)
#define down(i, x, y) for(int i = x; i >= y; i--)
#define maxn ((int)5e5 + 10)
#define INF 0x3f3f3f3f
//for(auto &c:S)cout<<c<<' ';
using namespace std;
typedef long long ll;
map<int, bool> mp;
int a[maxn], b[maxn], c[maxn], ans[maxn];
int main()
{
    int n, k, m, s; cin>>n>>k>>m>>s;
    int len = k + n - k * m;  //需要寻找的区间的长度
    int del = n - k * m;      //最多可以删除元素的个数
    int flag = 0;
    int ans_loc = 1;//左边界的初始位置,也是最后答案区间的左端点
    up(i, 1, n) scanf("%d", &a[i]);
    int type = 0;//记录有几种类型
    up(i, 1, s) //特殊花环需要的组成
    {
        int t; cin>>t;

        if(!b[t]) type++;//记录有几种类型

        ++b[t];//保留每种类型多少个,相当于每种类型的花需要的个数
        ++c[t];//保留每种类型多少个

        mp[t] = 1;//标记下
    }
    up(i, 1, len)  //先把算1 - len 这个区间,后面方便遍历
    {
        int t = a[i];
        if(mp[t]) b[t]--;  //如果发现出现了 s 中的类型,相应类型的个数就--
        if(b[t] == 0 && mp[t]) type--;  //如果这种类型的个数都有了 那么所需要的类型数就减少
        if(type == 0)  //如果满足题目条件,就记录位置并输出
        {
            ans_loc = 1;
            flag = 1;
            goto stop;
        }
    }
    for(int i = len + 1; i <= n; i++) //按 len 大小的区间长度往后遍历
    {
        ans_loc++;
        int l = a[ans_loc - 1];   //因为区间往右平移了,所以记录左边出去元素
        int r = a[i];//因为区间往右平移了,所以记录右边进来的元素
        if(mp[l]) //如果左边出去的元素,是咱们需要的,那么相应类型的花的需求数就++
        {
            b[l]++;
            if(b[l] == 1) type++; //如果需求数从无到有了,那么需要的类型的花的种类数也 +1
        }
        if(mp[r])  //同上
        {
            b[r]--;
            if(b[r] == 0) type--;
        }
        if(type == 0 && (ans_loc - 1) % k == 0)
 //如果所有的种类数都在这个区间里含有了,且这个区间前面的花的数目也是k的倍数,那么输出
        {
            flag = 1;
            goto stop;
        }
    }
    stop : {};
    int pp = 0;
    if(flag)
    {
        for(int i = ans_loc; i < len + ans_loc; i++) //遍历符合条件的额区间
        {
            int t = a[i];
            if(c[t] > 0) //如果是s里的就不可以删除
            {
                c[t]--;
                continue;
            }
            if(pp == del) break; //如果可以删除的数目满了就跳出
            ans[++pp] = i; //保留可以删除的点的编号

        }
        cout<<pp<<'\n';
        up(i, 1, pp)  //输出答案
        {
            printf("%d%c", ans[i], i == pp ? '\n' : ' ');
        }
    }
    else puts("-1");
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值