样例输入:
2
4 3 3
mark
1 4
5 7
3 8
1
10
12
7 3 3
creamii
2 3
3 4
2 9
9
11
12
样例输出:
m
a
r
e
a
r
题意:一开始给定一个长度为n的字符串,每次操作可以选定字符串中的一段区间,然后将这段区间复制后放入串的末尾,下一次操作是对上一次操作后的字符串进行操作。进行若干次操作后,进行一些询问,每次询问给定一个t,询问第t个字符是什么。
分析:数据是1E18.所以肯定不可能把字符串完整存下来,我们需要用一些技巧使得我们在一开始给定的s字符串中找到我们所需要询问的字符。首先我们可以确定的一点是由于每次操作都是在上一次操作的基础上复制一段,所以所有操作后形成的字符串中的字符都在s字符串中出现过。
我们把每次操作复制的字符单独看成一段,下面来分析一下如何找到位于第i段的某个字符在第i-1段上的位置。
假如我们当前字符的绝对位置是t,位于第i段上。sum[i]记录的是前i段操作后的总长度
那么t-sum[i-1]就是第t个字符相对于第i段的位置,那么我们在前i-1段找到第i次操作的区间,然后把l[i]~r[i]看作是第i段区间,但l[i]~r[i]实际上是在前i-1次操作形成的区间内的,那么字符t对应的位置就是l[i]-1+偏移地址(t-sum[i-1]),就这样我们就把第i段上的某个位置对应到了前i-1段,依次类推我们就可以找到位于第0段上的位置,直接输出即可。第0段就是我们一开始的s串。
注意不要忘记开long long。
下面是代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int N=3e5+10;
typedef long long ll;
ll l[N],r[N],sum[N];
char a[N];
int n,c,q;
char find(long long loc)
{
int id=lower_bound(sum+1,sum+c+1,loc)-sum;
if(id==1) return a[loc];
return find(loc-sum[id-1]+l[id]-1);
}
int main()
{
int T;
cin>>T;
while(T--)
{
scanf("%d%d%d",&n,&c,&q);
scanf("%s",a+1);
sum[1]=n;
l[1]=1;r[1]=n;
c++;
for(int i=2;i<=c;i++)
{
scanf("%lld%lld",&l[i],&r[i]);
sum[i]=sum[i-1]+r[i]-l[i]+1;
}
while(q--)
{
ll t;
scanf("%lld",&t);
printf("%c\n",find(t));
}
}
return 0;
}