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

题目描述

给定一个长度为 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,m100000 1 ≤ n , m ≤ 100000

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

Solution

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

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

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

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

  • 每次在 set s e t lower_bound() l o w e r _ b o u n d ( ) 计算前驱和后继的贡献即可。

  • 如果不想离散化就用个 map m a p 来存这 N N set

  • 时间复杂度 O(N log N) O ( N   l o g   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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值