LOJ #516. 「LibreOJ β Round #2」DP 一般看规律

本文介绍了一种高效算法,用于解决在一个序列中动态更新数字并计算最近重复元素间距离的问题。通过使用启发式合并策略和集合数据结构,实现了O(N log N)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

给定一个长度为 n 的序列 a,一共有 m 个操作。
每次操作的内容为:给定 x,y,序列中所有 x 会变成 y。

同时我们有一份代码:


int ans = 2147483647;
for (int i = 1; i <= n; i++) {
    for (int j = i + 1; j <= n; j++) {
        if (a[i] == a[j])
            ans = std::min(ans, j - i);
    }
}
std::cout << ans << std::endl;

请在每次修改后输出代码运行的结果。

输入格式

第一行两个数,表示 n,m。
第二行 n 个数,表示 a1,a2,⋯,an .
然后 m 行每行两个数 x 和 y,表示序列中所有 x 会变成 y。

输出格式

对于每次修改,输出答案。

样例

样例输入

5 10
2 7 6 3 8
6 1
7 1
1 3
5 6
1 7
9 5
1 10
7 6
7 5
3 9

样例输出

2147483647
1
1
1
1
1
1
1
1
1

数据范围与提示

1n,m1000001≤n,m≤100000

每个出现的数字绝对值在 int 范围内。

Solution

  • 题意:给出 MM 个操作,将长度为 N 序列中 所有的 某一个数字替换为另一个,

  • 询问每次操作后距离最近的两个 相同 数字的距离。

  • 由于每个位置上的数都只对它的 前驱和后继 产生影响,

  • 于是我们开 NNset (每个数一个),每次启发式合并,将较小的暴力合并到大的上面。

  • 每次在 setsetlower_bound()lower_bound() 计算前驱和后继的贡献即可。

  • 如果不想离散化就用个 mapmap 来存这 NNset

  • 时间复杂度 O(N log N)O(N log N) ,常数较大。

Code

#include<cstdio>
#include<algorithm>
#include<set>
#include<map>
#include<cctype>
using namespace std;
map<int,set<int> >mp;
int ans=2147483647;
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline void write(int x)
{
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
inline int min(int x,int y)
{
    return x<y?x:y;
}
inline void update(int x,int y)
{
    set<int>::iterator it=mp[x].lower_bound(y);
    if(it!=mp[x].end()) ans=min(ans,*it-y);
    if(it!=mp[x].begin()) ans=min(ans,y-*--it);
}
int main()
{
    int n=read(),m=read();
    for(int i=1;i<=n;i++)
    {
        int x=read();
        update(x,i);
        mp[x].insert(i);
    }
    while(m--)
    {
        int x=read(),y=read();
        if(x^y)
        {
            if(mp[x].size()>mp[y].size()) swap(mp[x],mp[y]);
            for(set<int>::iterator it=mp[x].begin();it!=mp[x].end();it++)
            {
                update(y,*it);
                mp[y].insert(*it);
            }
            mp[x].clear();
        }
        write(ans),putchar('\n');   
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值