1 条题解

  • 0
    @ 2026-9-26 11:53:43

    发现题解区都是奇偶性什么什么的,于是提供一种新的,我认为更简单,更好理解的解法。


    首先数字总是 11 或 22 这一点肯定特别有用。

    我们先钦定答案的左端点是 11,右端点可以在前缀和上二分到第一个 ≥x\ge x 的位置 rr。

    如果刚好满足和为 xx 那就不用管。否则:

    令 1∼r1 \sim r 的和为 yy,那么肯定有 y=x+1y = x + 1 且 ar=2a_r = 2。

    证明:

    若 y=x+k,k≥2y = x + k, k \ge 2,那么必然可以通过减少 rr 使得答案更小。

    所以 y=x+1y = x + 1。

    若此时 ar=1a_r = 1,那么必然可以通过令 r←r−1r \gets r - 1 使得更加满足答案。

    证毕。

    然后,正常情况下,我们肯定需要通过双指针检查最小满足的 l,rl, r。

    但是不难发现,

    1. 一旦在右边 +1+1,左边 −2-2(即 ar+1=1,al=2a_{r + 1} = 1, a_l = 2)时,就相当于给 yy 减一了。

    2. 如果刚刚好满足给左边 −1-1 右边不动(即 al=1a_l = 1),也相当于给 yy 减一。

    那么左右端点必须有 11。那么反过来,我只需要知道 1,r1, r 向右有多少个连续的 22 就行了,即每次有 al=2,ar+1=2a_l = 2, a_{r + 1} = 2 的时候肯定要 l←l+1,r←r+1l \gets l + 1, r \gets r + 1。

    更详细的,令 fif_i 表示 ii 往右有多少个连续的 22。

    如果 f1≥frf_1 \ge f_r,那么肯定满足上述的第一种情况。

    如果 f1<frf_1 < f_r,那么就是上述的第二种情况。

    然后代码就会清新很多。

    for(int i = n;i >= 1;i--){
        if(a[i] == 'W') f[i] = 0, s[i] = 1;
        else f[i] = f[i+1] + 1, s[i] = 2;
    }
    
    for(int i = 1;i <= n;i++) s[i] += s[i-1];
    
    while(q--){
        int x; cin>>x;
        int l = 1, r = n, ans = -1;
        while(l <= r){
            int mid = (l + r) >> 1;
            if(s[mid] >= x) r = mid - 1, ans = mid;
            else l = mid + 1;
        }
        if(ans == -1){
            cout<<"NIE\n"; continue;
        }
        if(s[ans] == x){
            cout<<"1 "<<ans<<'\n'; continue;
        }
        if(f[1] >= f[ans]){
            if(ans + f[ans] > n) cout<<"NIE\n";
            else cout<<1 + f[ans]<<" "<<ans + f[ans]<<'\n';
            continue;
        }
        if(ans + f[1] > n) cout<<"NIE\n";
        else cout<<2 + f[1]<<" "<<ans + f[1]<<'\n';
    }
    
    • 1

    信息

    ID
    3882
    时间
    600ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者