2 条题解

  • 1
    @ 2024-8-26 9:13:59

    【MX-J3-T2】Substring

    更好的阅读体验

    题意简述

    给你 11nn排列,求字典序第 kk 小的连续子串。

    思路分析

    以下“区间”,“子串”均指连续子串

    求第 kk 小,一个朴素的想法就是排序所有区间,然后输出第 kk 呗。

    这样的时间复杂度是 Θ(n2+q)\Theta(n^2+q) 的,代码繁杂,此处不赘述。

    但是我们这样考虑: 钦定一个区间,比如 [i,n][i,n] ,由字典序的定义,他的所有前缀 i<j<n\forall i<j<n , [i,j][i,j] 的字典序都小于他。也就是说,把所有区间排序输出, [i,n][i,n] 一定在他的所有前缀后面。

    [i,n][i,n] 的所有前缀有 nin-i 个,也就是说以 ii 开头的所有子串的字典序排名是连续的,有 ni+1n-i+1 个。

    然后考虑区间左端点不同的情况,由于是排列,左端点不同就是最左的数字不同。所以我们可以先排个序,然后维护一个类似前缀和的数组 sum[i]\text{sum}[i] 表示区间 [i,n][i,n] 的字典序排名。询问时在 sum[i]\text{sum}[i] 数组中二分左端点,快速计算右端点(即在左端点加上偏移量 ksum[pos1]1k-\text{sum}[pos-1]-1 )即可。

    排序之前要把原下标记录一下。

    代码

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    int n,q,sum[300005];
    pair<int,int>a[300005];
    signed main(){
        ios::sync_with_stdio(false);
        cin.tie(0),cout.tie(0);
        cin>>n>>q;
        for(int i=1;i<=n;i++)cin>>a[i].first,a[i].second=i;
        sort(a+1,a+n+1);
        for(int i=1;i<=n;i++)
            sum[i]=sum[i-1]+n-a[i].second+1;
        for(int x;q--;){
            cin>>x;
            int pos=lower_bound(sum+1,sum+n+1,x)-sum;
            x-=sum[pos-1];
            cout<<a[pos].second<<" "<<a[pos].second+x-1<<"\n";
        }
        return 0;
    }
    

    信息

    ID
    42
    时间
    1000ms
    内存
    512MiB
    难度
    3
    标签
    递交数
    542
    已通过
    175
    上传者