CF1073C 题解——尺取(本人原稿在luogu)

本文解析了CF1073C问题,涉及在一个二维坐标系中通过修改字符串中的字符(U/D/L/R)来达到特定位置。使用二分法确定最小修改子串长度,利用尺取概念分析操作规则,确保操作连续且不会影响结果。

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

CF1073C题解

在洛谷食用更佳

luogu博客

简化题意

已知一字符串 s s s 长度为 n n n,且 s s s 中各字符代表的意义如下。


  • U U U 向上走一步;
  • D D D 向下走一步;
  • L L L 向左走一步;
  • R R R 向右走一步;

现在你在平面直角坐标系的原点,你需要改变字符串 s s s 中的某个子串中的字符,使得最后能到 ( x , y ) (x,y) (x,y) 位置。求这个子串的最小长度。


思路

二分


首先,二分的要求:有界性、单调性、二段性。

本题中,我们设答案为 l l l(固定),则所有大于 l l l 的答案都可行,也就是说一定可以通过改变长度大于 l l l 的子串以到达目标点(比如你可以让多余的子串里选一些 R R R 改成 L L L,相同数量的 L L L 改成 R R R,相当于没改……)。

显然,本题满足二分的条件。

图1 二分的单调性

由于题解里面有很多二分的做法,所以在这里我就稍微带一下,代码就不传了。

时间复杂度 O ( n log ⁡ n ) O(n\log{n}) O(nlogn)


尺取

尺取的概念、模板题等详见【洛谷日报#73】尺取法小结

这里我先简单说一下尺取的基本内容。


尺取的要求
  • 取的是一段连续的区间(有两个端点);

  • 结果不会因区间的变长而由成立变成不成立;

  • 左端点右移,右端点不会往左移。

要求一是本题的条件,要求二前面已经证明过。所以本题满足尺取的条件。


方法
  1. 按照题意,算出实际上最后到达的位置,并设答案为 n + 1 n + 1 n+1

  2. 特判不用改动就能到的情况;

  3. 设左端点为 l l l,右端点为 r r r,并赋初始值 1 1 1

  4. 从左到右枚举每个右端点,被枚举到的点取消 s r s_r sr 的对应操作。如果区间长度大于等于当前位置与目标点的曼哈顿距离,就右移左端点;

  5. 对于每次右移左端点,如果当前区间长度两点的曼哈顿距离同奇同偶,更新答案;

  6. “回溯”,更新答案后,恢复 s l s_l sl 的操作;

  7. 若答案为 n + 1 n + 1 n+1,说明无解。


证明

为什么判断是否右移左端点只要看曼哈顿距离:类比一下“撤回”操作。取消 s r s_r sr 的操作后,可以修改成向任意方向走一步,此时相当于从 l − 1 l - 1 l1 位置开始,看走 r − l + 1 r - l + 1 rl+1 步能否到达目标点。设曼哈顿距离为 p p p,则:

  • p = r − l + 1 p = r - l + 1 p=rl+1 时,正好能走到目标点;

  • p ≤ r − l + 1 p \leq r - l + 1 prl+1 p ≡ r − l + 1 ( m o d 2 ) p \equiv r - l + 1 \pmod 2 prl+1(mod2) 时,可以通过“一上一下,一左一右”走到目标点。

图2 满足题目要求的条件

我们发现,有用的步数就是曼哈顿距离 p p p。多往某个方向走一步,就得多往相反的方向走一步,所以多余的步数一定是偶数。由于任何一个整数加偶数,奇偶性不变,所以当且仅当曼哈顿距离 p p p 和区间长度 r − l + 1 r - l + 1 rl+1 同奇同偶时,修改后可以到达目标点。

时间复杂度 O ( n ) O(n) O(n)

上代码!

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
char s[N];
int main()
{
    int sx=0,sy=0;
    int n;
    scanf("%d",&n);
    scanf("%s",s+1);
    int fx,fy;
    scanf("%d%d",&fx,&fy);
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='U')
            ++sy;
        else if(s[i]=='D')
            --sy;
        else if(s[i]=='L')
            --sx;
        else
            ++sx;//计算最终位置 。 
    }
    if(sx==fx&&sy==fy)
        return 0*printf("0\n");//特判不用修改的情况 。 
    int l=1,minn=n+1;
    for(int r=1;r<=n;++r)
    {
        if(s[r]=='U')
            --sy;
        else if(s[r]=='D')
            ++sy;
        else if(s[r]=='L')
            ++sx;
        else
            --sx;//枚举右端点,取消s[r]操作 。 
        while(l<=r&&abs(fx-sx)+abs(fy-sy)<=r-l+1)
        {
            if(((abs(fx-sx)+abs(fy-sy)))%2==(r-l+1)%2)
                minn=min(minn,r-l+1);
            if(s[l]=='U')
                ++sy;
            else if(s[l]=='D')
                --sy;
            else if(s[l]=='L')
                --sx;
            else
                ++sx;//枚举左端点,恢复s[l]操作。
            ++l;
        }
    }
    printf("%d\n",minn==n+1?-1:minn);
    return 0;
}
//陈文忻 2023-10-22。 
//诚信做题,拒绝ctrl C+ctrl V。 

AC 啦!

讲得不是很详细,自己意会吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值