Mark and His Unfinished Essay题解

这题的rating是1400,对于我这种蒟蒻来说,还是稍微有点难度的QAQ

但是这题没有涉及任何算法,纯纯的思维题,还是有点巧妙的(有点类似KMP的思路,使用跳转数组)

朴素的暴力

最初的字符串为 m a r k mark mark

第一次拼接的结果为 m a r k m a r k markmark markmark

第二次拼接的结果为 m a r k m a r k m a r markmarkmar markmarkmar

第三次拼接的结果为 m a r k m a r k m a r r k m a r k markmarkmarrkmark markmarkmarrkmark

那么,我们可以试试直接拼接

long long l,r;
cin >> l >> r;
for(long long i = l - 1;i < r;++i)
    str += str[i];

看上去可行,我们来提交看看

wa

不出所料,看看数据的范围就知道不太可能。所以我们想想不同的写法。在这里,我们可以试试类似KMP的跳转数组,当这个地方满足某个条件的时候,就可以跳转到前边的某个坐标

那么我们来看看这个思路的具体实现方式

跳转坐标

首先我想的方法是创建一个数组, n u m [ N ] num[N] num[N],不直接去存储字符串,而是存储跳转的坐标,如:

{0,0,0,0,-4,-4,-4,-4,-4,-4,-4,-8,-8,-8,-8,-8,-8}

在读取一个坐标的时候,不停地往前跳转,一直跳转到跳转数组的值为0的地方,就是我们想要读取的字符

看起来可行,但是…………,这不还是要存储大量的数据吗,只不过从字符变成了数字

emmmm…………,不过还是有可取的地方的,至少跳转数组这个想法是可行的。还需要进一步的优化,于是我注意到了

0,0,0,0,-4,-4,-4,-4,-4,-4,-4,-8,-8,-8,-8,-8,-8

将原有的数组分成四部分,首先是原有的部分,然后分别为第一、二、三次拼接的部分。可以注意到,同一次拼接当中的跳转位数都是相同的,我们可以将同一次的拼接全部放在一个数组中,如:(这里就不写原位置了)

-4,-4,-8

看样子可行,那么需要解决的问题就剩下一个了:如何确定我想要输出的下标在哪个区域内呢?

当下标为 [ 5 , 8 ] [5,8] [5,8]的时候,在第一个区域;下标为 [ 9 , 11 ] [9,11] [9,11]的时候,在第二个区域;下标为 [ 12 , 17 ] [12,17] [12,17]的时候,在第三个区域

那么,将每个区域表示的下标的最大值存储下来,每次查找的时候使用二分来确定在哪个区域即可

来看看结果吧

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define all(a) a.begin(), a.end()
#define vint vector<int>
#define pb push_back

int n,m,q;
string s;

void solve()
{
    cin >> n >> m >> q;
    cin >> s;
    vint j1(m),j2(m);
    int len = s.size();
    for(int i = 0;i < m;++i)
    {
    	int l,r;
    	cin >> l >> r;
    	len += r - l + 1;
    	j1[i] = len;
    	j2[i] = len - r;
    }
    
    while(q--)
    {
    	int x;
    	cin >> x;
    	while(x > s.size())
    	{
    		int l = 0,r = m - 1;
    		while(l < r)
    		{
    			int mid = l + r >> 1;
    			if(j1[mid] < x)
    				l = mid + 1;
    			else
    				r = mid;
    		}
    		x -= j2[l];
    	}
    	cout << s[x - 1] << endl;
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int t = 1;
    cin >> t;
    while (t--)
        solve();

    return 0;
}

提交:

ac

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值