# NOI2015模拟SXK 字符串游戏 后缀数组预处理+主席树查询

### 题目大意

1. 给你两个整数k1$k1$k2$k2$，询问在所有T$T$不相等的字符串中，字典序从小到大排序，排在第k1$k1$位的字符串T[l,r]$T[l ,r]$，如果该子串出现了多次，则询问起始位置第k2$k2$小的那个。输出询问的那个子串的起始位置和终止位置（即l$l$r$r$
2. 给定两个整数l,r$l, r$，询问子串T[l,r]$T[l, r]$T$T$中不相同子串字典序排名k1$k1$，以及在相同子串中从小到大的起始位置排名k2$k2$

N500000$N\leq 500000$M100000$M\leq 100000$

### 程序

//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;
typedef long long LL;

const int MAXN = 5e5 + 5;

struct Tree {int l, r, Cnt;} Tr[MAXN * 20];

int Len, M, SA[MAXN], rank[MAXN], Height[MAXN], tax[MAXN], tp[MAXN];
int tot, N, Log[MAXN], Root[MAXN], Rmq[MAXN][21];
LL Sum[MAXN];
char S[MAXN];

void RSort() {
for (int i = 0; i <= M; i ++) tax[i] = 0;
for (int i = 1; i <= Len; i ++) tax[rank[tp[i]]] ++;
for (int i = 1; i <= M; i ++) tax[i] += tax[i - 1];
for (int i = Len; i >= 1; i --) SA[tax[rank[tp[i]]] --] = tp[i];
}

bool cmp(int *f, int x, int y, int w) { return f[x] == f[y] && f[x + w] == f[y + w];}

void Suffix() {
Len = strlen(S + 1);
for (int i = 1; i <= Len; i ++) rank[i] = S[i], tp[i] = i;
M = 127, RSort();

for (int i, p = 1, w = 1; p < Len; w += w, M = p) {
for (p = 0, i = Len - w + 1; i <= Len; i ++) tp[++ p] = i;
for (i = 1; i <= Len; i ++) if (SA[i] > w) tp[++ p] = SA[i] - w;
RSort(), swap(rank, tp), p = rank[SA[1]] = 1;
for (i = 2; i <= Len; i ++) rank[SA[i]] = cmp(tp, SA[i], SA[i - 1], w) ? p : ++ p;
}

int j, k = 0;
for (int i = 1; i <= Len; Height[rank[i ++]] = k)
for (k = k ? k - 1 : k, j = SA[rank[i] - 1]; S[i + k] == S[j + k]; k ++);
}

void Insert(int &Now, int l, int r, int Rt, int Side) {
Now = ++ tot;
Tr[Now] = Tr[Rt];
Tr[Now].Cnt ++;
if (l == r) return;
int Mid = (l + r) >> 1;
if (Side <= Mid) Insert(Tr[Now].l, l, Mid, Tr[Rt].l, Side); else
Insert(Tr[Now].r, Mid + 1, r, Tr[Rt].r, Side);
}

int Query(int l, int r, int Rank, int Rl, int Rr) {
if (l == r) return l;
int Mid = (l + r) >> 1;
int ll = Tr[Rl].l, lr = Tr[Rl].r, rl = Tr[Rr].l, rr = Tr[Rr].r;
if (Tr[rl].Cnt - Tr[ll].Cnt >= Rank) return Query(l, Mid, Rank, ll, rl); else
return Query(Mid + 1, r, Rank - Tr[rl].Cnt + Tr[ll].Cnt, lr, rr);
}

int GetNum(int l, int r, int lx, int rx, int Rl, int Rr) {
if (rx < l || lx > r) return 0;
if (lx <= l && rx >= r) return Tr[Rr].Cnt - Tr[Rl].Cnt;
int Mid = (l + r) >> 1;
return GetNum(l, Mid, lx, rx, Tr[Rl].l, Tr[Rr].l) + GetNum(Mid + 1, r, lx, rx, Tr[Rl].r, Tr[Rr].r);
}

int Min(int l, int r) {
int Len = Log[r - l + 1];
return min(Rmq[l][Len], Rmq[r - (1 << Len) + 1][Len]);
}

void Prepare() {
for (int i = 1; i <= Len; i ++) Insert(Root[i], 1, Len, Root[i - 1], SA[i]);

for (int i = 1, j = 0; i <= Len; i <<= 1, j ++) Log[i] = j;
for (int i = 2; i <= Len; i++) Log[i] = max(Log[i - 1], Log[i]);

for (int i = 1; i <= Len; i ++) Rmq[i][0] = Height[i];
for (int j = 1; j <= 20; j ++)
for (int i = 1; i <= Len - (1 << (j - 1)) + 1; i ++)
Rmq[i][j] = min(Rmq[i][j - 1], Rmq[i + (1 << (j - 1))][j - 1]);

for (int i = 1; i <= Len; i ++) Sum[i] = Sum[i - 1] + Len - SA[i] + 1 - Height[i];
}

int GetL(int Now, int len) {
int l = 1, r = Now - 1, Ans = Now;
while (l <= r) {
int Mid = (l + r) >> 1;
if (Min(Mid + 1, Now) >= len) Ans = Mid, r = Mid - 1; else l = Mid + 1;
}
return Ans;
}

int GetR(int Now, int len) {
int l = Now + 1, r = Len, Ans = Now;
while (l <= r) {
int Mid = (l + r) >> 1;
if (Min(Now + 1, Mid) >= len) Ans = Mid, l = Mid + 1; else r = Mid - 1;
}
return Ans;
}

void Solve1(LL k1, LL k2) {
int l = 1, r = Len, Ans = 0;
while (l <= r) {
int Mid = (l + r) >> 1;
if (Sum[Mid] < k1) Ans = Mid, l = Mid + 1; else r = Mid - 1;
}
int len = k1 - Sum[Ans] + Height[++ Ans];
l = GetL(Ans, len), r = GetR(Ans, len);
if (l == r) {
printf("%d %d\n", SA[Ans], SA[Ans] + len - 1);
return;
}
Ans = Query(1, Len, k2, Root[l - 1], Root[r]);
printf("%d %d\n", Ans, Ans + len - 1);
}

void Solve2(LL l, LL r) {
int len = r - l + 1, start = l - 1, Rank = rank[l];
r = GetR(Rank, len), l = GetL(Rank, len);
LL k1 = Sum[l - 1] + len - Height[l];
printf("%lld %d\n", k1, GetNum(1, Len, 1, start, Root[l - 1], Root[r]) + 1);
}

int main() {
freopen("4150.in", "r", stdin), freopen("4150.out", "w", stdout);

scanf("%s", S + 1);
Suffix();
Prepare();
scanf("%d", &N);
for (int i = 1; i <= N; i ++) {
int Ord; LL l, r;
scanf("%d%lld%lld", &Ord, &l, &r);
if (Ord == 1) Solve1(l, r); else Solve2(l, r);
}
}

08-05 571

03-11 86

12-16 16

02-06 386

02-07 66

11-06 4010

05-07 818

01-11 745

05-01 2092

11-01 71

#### OpenJudge NOI题库 6266:取石子游戏

©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

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