Machine Learning CodeForces - 940F(带修改的莫队)

题解原文地址:https://www.cnblogs.com/lujiaju6555/p/8468709.html

给数组a,有两种操作,1 l r查询[l,r]中每个数出现次数的mex,注意是出现次数,mex是最小未出现的自然数,2 x y将a[x]修改为y。

题解:带修改莫队可以解决此题。带修改莫队不会的同学可以先去做下BZOJ2120,然后mex+莫队可以参考BZOJ3585。带修改莫队就是加入了第三关键字time,然后按(左端点所在块,右端点所在块,时间)排序,其中时间指的是在第几次修改操作后。注意修改时要记下原来的数,以便还原回去。维护mex可以对权值分块,如果某块中数的个数==R-L+1,那么这块所有数都出现了,否则暴力扫,我有个同学直接暴力维护也过了。。。

#include <bits/stdc++.h>
#define MOD 2018
#define LL long long
#define ULL unsigned long long
#define Pair pair<int, int>
#define mem(a, b) memset(a, b, sizeof(a))
#define _  ios_base::sync_with_stdio(0),cin.tie(0)
//freopen("1.txt", "r", stdin);
using namespace std;
const int maxn = 200005, INF = 0x7fffffff;
int n, m, pos[maxn], s[maxn], c[maxn], all[maxn], t[maxn], cnt[maxn];
int qsz, csz;
struct node
{
    int l, r, t, res, id;
}Node[maxn];

void add_n(int l, int r, int t, int id)
{
    Node[id].l = l;
    Node[id].r = r;
    Node[id].t = t;
    Node[id].id = id;
}

struct change
{
    int pos, New, Old;
}Cha[maxn];

void add_c(int pos, int New, int Old, int ans)
{
    Cha[ans].pos = pos;
    Cha[ans].New = New;
    Cha[ans].Old = Old;
}

bool cmp(node a, node b)
{
    if(pos[a.l] == pos[b.l])
    {
        if(pos[a.r] == pos[b.r])
            return a.t < b.t;
        return pos[a.r] < pos[b.r];
    }
    return pos[a.l] < pos[b.l];
}

bool cmp_id(node a, node b)
{
    return a.id < b.id;
}

int update(int val, int d)
{
    if(s[val] > 0) cnt[s[val]]--;    //s是记录val 出现的次数  cnt标记这个次数是否出现  因为有多个数 可能有些数出现的次数相同 所有用++即可
    s[val] += d;                     //因为当前数val的次数改变 所以 如果未改变时的val的次数 给cnt贡献了1个的话 要先减去 再更新s[val] 再更新cnt[s[val]]
    if(s[val] > 0) cnt[s[val]]++;
}
int L=1, R=0, T=0;
int go(int idx, int val)
{
    if(L <= idx && idx <= R)  //如果 当前时间内 修改的位置在当前区间 则先删去上一次在这个位置更新的值 再加上本次在这个位置更新的值
    {
        update(c[idx], -1);
        update(val, 1);
    }
    c[idx] = val;     //更新
}


int main()
{
    qsz = csz = 0;
    int tot = 0;
    scanf("%d%d", &n, &m);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&c[i]);
        t[i] = c[i];
        all[++tot] = c[i];
    }
    int block=pow(n,2.0/3);
    for(int i=1; i<=n; i++)
        pos[i] = (i-1)/block + 1;
    for(int i=1; i<=m; i++)
    {
        int op, l, r;
        scanf("%d%d%d", &op, &l, &r);
        if(op == 1)
        {
            add_n(l, r, csz, ++qsz);
        }
        else
        {
            add_c(l, r, t[l], ++csz);
            t[l] = r;
            all[++tot] = r;
        }
    }
    sort(all+1, all+tot+1);
    tot = unique(all+1, all+tot+1) - (all + 1);
    for(int i=1; i<=n; i++)
        c[i] = lower_bound(all+1, all+tot+1, c[i]) - all;
    for(int i=1; i<=csz; i++)
    {
        Cha[i].New = lower_bound(all+1, all+tot+1, Cha[i].New) - all;
        Cha[i].Old = lower_bound(all+1, all+tot+1, Cha[i].Old) - all;
    }
    sort(Node+1, Node+qsz+1, cmp);
    for(int i=1; i<=qsz; i++)
    {
//        for(; T < Node[i].t; T++)
//            go(Cha[T+1].pos, Cha[T+1].New);
//        for(; T > Node[i].t; T--)
//            go(Cha[T].pos, Cha[T].Old);
        for(; R < Node[i].r; R++)
            update(c[R+1], 1);
        for(; R > Node[i].r; R--)
            update(c[R], -1);
        for(; L < Node[i].l; L++)
            update(c[L], -1);
        for(; L > Node[i].l; L--)
            update(c[L-1], 1);
        for(; T < Node[i].t; T++)       //遍历在询问当前区间时 的 时间之前的修改
            go(Cha[T+1].pos, Cha[T+1].New);
        for(; T > Node[i].t; T--)
            go(Cha[T].pos, Cha[T].Old);

        for(int j=1; ; j++)
            if(!cnt[j])
            {
                Node[i].res = j;
                break;
            }
     //   cout<< Node[i].res <<endl;
    }
    sort(Node+1, Node+qsz+1, cmp_id);
    for(int i=1; i<=qsz; i++)
        cout<< Node[i].res <<endl;

    return 0;
}

 

转载于:https://www.cnblogs.com/WTSRUVF/p/9346960.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值