版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/86560478
【比赛链接】
【题解链接】
**【A】**Sum in the tree
【思路要点】
- 题目中给出的限制条件相当于限定了所有深度为奇数的点及其父亲的权值和,显然,将深度为偶数的非叶节点的权值设置得尽可能大有利于减少全局权值和。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const int INF = 2e9; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } ll ans; int n, s[MAXN]; vector <int> a[MAXN]; void work(int pos, int last) { if (s[pos] != -1) { if (s[pos] < last) assert(false); ans += s[pos] - last; last = s[pos]; } else { int Min = INF; for (auto x : a[pos]) chkmin(Min, s[x]); if (Min < last) { puts("-1"); exit(0); } else if (Min == INF) Min = last; ans += Min - last; last = Min; } for (auto x : a[pos]) work(x, last); } int main() { read(n); for (int i = 2; i <= n; i++) { int x; read(x); a[x].push_back(i); } for (int i = 1; i <= n; i++) read(s[i]); work(1, 0); writeln(ans); return 0; }
**【B】**Nice table
【思路要点】
- 可以发现,合法的矩阵要么每一行都只有两种字符,要么每一列都只有两种字符。
- 因此,我们枚举左上角的 的矩形中的字符分布,并枚举以上两种情况的一种,代入计算最优解即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 3e5 + 5; const char rev[5] = {' ', 'A', 'C', 'G', 'T'}; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, Min, r[MAXN][2][5], c[MAXN][2][5]; bool vis[5]; vector <int> ans[MAXN], s[MAXN]; int verans() { int res = 0; for (int i = 1, now = 1, nxt = 2; i <= n; i++, swap(now, nxt)) res += m - max(r[i][0][ans[now][1]] + r[i][1][ans[now][2]], r[i][0][ans[now][2]] + r[i][1][ans[now][1]]); return res; } int horans() { int res = 0; for (int i = 1, now = 1, nxt = 2; i <= m; i++, swap(now, nxt)) res += n - max(c[i][0][ans[1][now]] + c[i][1][ans[2][now]], c[i][0][ans[2][now]] + c[i][1][ans[1][now]]); return res; } void output() { for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) putchar(rev[ans[i][j]]); putchar('\n'); } exit(0); } void getverans() { int tmp[3][3]; for (int i = 1; i <= 2; i++) for (int j = 1; j <= 2; j++) tmp[i][j] = ans[i][j]; for (int i = 1, now = 1, nxt = 2; i <= n; i++, swap(now, nxt)) { if (r[i][0][tmp[now][1]] + r[i][1][tmp[now][2]] > r[i][0][tmp[now][2]] + r[i][1][tmp[now][1]]) { for (int j = 1; j <= m; j++) if (j & 1) ans[i][j] = tmp[now][2]; else ans[i][j] = tmp[now][1]; } else { for (int j = 1; j <= m; j++) if (j & 1) ans[i][j] = tmp[now][1]; else ans[i][j] = tmp[now][2]; } } output(); } void gethorans() { int tmp[3][3]; for (int i = 1; i <= 2; i++) for (int j = 1; j <= 2; j++) tmp[i][j] = ans[i][j]; for (int i = 1, now = 1, nxt = 2; i <= m; i++, swap(now, nxt)) { if (c[i][0][tmp[1][now]] + c[i][1][tmp[2][now]] > c[i][0][tmp[2][now]] + c[i][1][tmp[1][now]]) { for (int j = 1; j <= n; j++) if (j & 1) ans[j][i] = tmp[2][now]; else ans[j][i] = tmp[1][now]; } else { for (int j = 1; j <= n; j++) if (j & 1) ans[j][i] = tmp[1][now]; else ans[j][i] = tmp[2][now]; } } output(); } void getans(int x, int y) { if (x == 3) { if (Min == verans()) getverans(); if (Min == horans()) gethorans(); return; } int tx = x, ty = y + 1; if (ty == 3) ty = 1, tx++; for (int i = 1; i <= 4; i++) if (!vis[i]) { vis[i] = true; ans[x][y] = i; getans(tx, ty); vis[i] = false; } } void work(int x, int y) { if (x == 3) { chkmin(Min, verans()); chkmin(Min, horans()); return; } int tx = x, ty = y + 1; if (ty == 3) ty = 1, tx++; for (int i = 1; i <= 4; i++) if (!vis[i]) { vis[i] = true; ans[x][y] = i; work(tx, ty); vis[i] = false; } } int main() { read(n), read(m); s[0].resize(m + 2); s[n + 1].resize(m + 2); for (int i = 1; i <= n; i++) { s[i].resize(m + 2); ans[i].resize(m + 1); static char tmp[MAXN]; scanf("\n%s", tmp + 1); for (int j = 1; j <= m; j++) { if (tmp[j] == 'A') s[i][j] = 1; if (tmp[j] == 'C') s[i][j] = 2; if (tmp[j] == 'G') s[i][j] = 3; if (tmp[j] == 'T') s[i][j] = 4; r[i][j & 1][s[i][j]]++; c[j][i & 1][s[i][j]]++; } } Min = n * m + 5; work(1, 1); //cerr << Min << endl; getans(1, 1); return 0; }
**【C】**Construct a tree
【思路要点】
- 同样可以被看做所有节点的深度和。
- 无论是几叉树, 的最大值均为 ,更大的 一定是无解的。
- 可以发现,树的叉数越多,可能取到的情况越多,因此考虑从小到大枚举树的叉数 ,判断是否有解。
- 将树构造为完全 叉树可以最小化所有节点的深度和,若 不足该值,则需要将 取到更大。
- 否则,我们考虑每次将尚未移动过的一个最深的叶子取下,接在最深的叶子下面,若这样的操作使所有节点的深度和超过 ,则改为将该叶子接在使得所有节点的深度和等于 的位置。
- 可以发现,每一次被移动的叶子的深度均会增加,并且新增的深度最终至多只会具有两个节点,因此对于所有 不小于完全 叉树中所有节点的深度和的情况,我们都可以构造出一组解。
- 判断是否有解的时间复杂度为 ,总时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, ans[MAXN]; ll s; bool solve(int k) { static ll cnt[MAXN]; ll now = 1; int depth = 1; cnt[depth] = 1; while (now < n) { cnt[depth + 1] = cnt[depth] * k, depth++; now += cnt[depth]; } cnt[depth] -= now - n; ll cursum = 0; for (int i = 1; i <= depth; i++) cursum += 1ll * i * cnt[i]; if (cursum > s) return false; puts("Yes"); int tepth = depth; while (cursum < s) { while (cnt[tepth] == 1) tepth--; if (cursum + depth + 1 - tepth <= s) cnt[++depth] = 1, cnt[tepth]--, cursum += depth - tepth; else cnt[tepth]--, cnt[s - cursum + tepth]++, cursum = s; } for (int i = 1, start = 1; i <= depth - 1; start += cnt[i], i++) { int pos = start, lft = k; for (int j = start + cnt[i]; j < start + cnt[i] + cnt[i + 1]; j++) { ans[j] = pos; if (--lft == 0) { pos++; lft = k; } } } for (int i = 2; i <= n; i++) printf("%d ", ans[i]); return true; } int main() { read(n), read(s); if (s > 1ll * n * (n + 1) / 2) { puts("No"); return 0; } for (int i = 1; i <= n; i++) if (solve(i)) return 0; puts("No"); return 0; }
**【D】**Eels
【思路要点】
- 我们称一次危险的打斗 是 产生的。
- 可以发现如果我们将所有鱼的大小进行排序,第一条鱼不会产生危险的打斗,一条比其之前所有鱼大小之和的两倍更大的鱼不会产生危险的打斗。
- 并且我们可以构造出一种方案使得不满足上述两点的鱼均产生一次危险的打斗。
- **证明:**考虑每次让最小的两条鱼 发生打斗,若该打斗是不危险的,即 ,那么 一定没有吃过任意一条鱼,否则 将不会是最小的两条鱼之一,因此, 一定是满足比其之前所有鱼大小之和的两倍更大的鱼。
- 考虑将值域分为若干区间,使得区间最小值 和最大值 满足 ,不妨分为 ,不难发现,只有区间最小值才有可能成为比其之前所有鱼大小之和的两倍更大的鱼,对每个区间分别用堆维护即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXLOG = 31; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct Heap { int cnt; ll sum; priority_queue <int, vector <int>, greater <int> > Heap, Delt; void ins(int x) { cnt++, sum += x; Heap.push(x); } void del(int x) { cnt--, sum -= x; Delt.push(x); } int query() { while (!Heap.empty() && !Delt.empty() && Heap.top() == Delt.top()) { Heap.pop(); Delt.pop(); } if (Heap.empty()) return -1; else return Heap.top(); } } a[MAXLOG]; int main() { int n; read(n); for (int i = 1; i <= n; i++) { char c; int x; scanf("\n%c %d", &c, &x); for (int i = 0; i < MAXLOG; i++) if (x <= (1 << i)) { if (c == '+') a[i].ins(x); else a[i].del(x); break; } int ans = 0; ll sum = 0; for (int i = 0; i < MAXLOG; i++) { ans += a[i].cnt; int tmp = a[i].query(); if (tmp > 2 * sum) ans--; sum += a[i].sum; } writeln(ans); } return 0; }
**【E】**Fedya the Potter
【思路要点】
- 枚举 数组中区间的左端点,右端点对应的区间 至多变化 次,通过 表 二分,我们可以得到表示为一段段相同元素的 数组。
- 用二分答案找中位数,我们剩余的问题在于计算 数组中 以内数的个数。
- 在 数组上 类欧几里得即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 5e4 + 5; const int MAXM = 1e6 + 5; const int MAXLOG = 18; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, tot, a[MAXN]; pair <int, int> b[MAXM]; namespace rmq { int g[MAXN][MAXLOG], Log[MAXN]; int query(int l, int r) { int len = r - l + 1, tmp = Log[len]; return __gcd(g[l][tmp], g[r - (1 << tmp) + 1][tmp]); } void init() { for (int i = 1; i <= n; i++) { g[i][0] = a[i]; Log[i] = Log[i - 1]; if ((1 << (Log[i] + 1)) <= i) Log[i]++; } for (int t = 1; t < MAXLOG; t++) for (int i = 1, j = (1 << (t - 1)) + 1; j <= n; i++, j++) g[i][t] = __gcd(g[i][t - 1], g[j][t - 1]); } } ll func(int x, int y) { ll ans = y * (y + 1ll) / 2; ans += 1ll * y * (x - y); return ans; } ll solve(ll n, ll a, ll b, ll c) { if (a == 0) return (c / b) * (n + 1); else if (a >= b || c >= b) return solve(n, a % b, b, c % b) + n * (n + 1) / 2 * (a / b) + (n + 1) * (c / b); else { ll m = (n * a + c) / b; return n * m - solve(m - 1, b, a, b - c - 1); } } ll rangecnt(ll sum, ll a, ll b, ll n, ll m) { if (sum < 0) return 0; ll ans = 0; if (sum >= b * m) { ll tmp = (sum - b * m) / a; if (tmp >= n) return (n + 1) * (m + 1); ans += (tmp + 1) * (m + 1); sum -= (tmp + 1) * a; n -= tmp + 1; } if (sum < 0) return ans; chkmin(n, sum / a); ll c = sum - n * a; return ans + n + 1 + solve(n, a, b, c); } ll getcnt(ll sum) { ll ans = 0; for (int i = 1; i <= m; i++) ans += func(b[i].second, min(sum / b[i].first, 0ll + b[i].second)); ll len = 0, cnt = 0; for (int i = 1, j = 1; i <= m; i++) { while (j < i && len + 1ll * b[i].first * b[i].second > sum) { cnt -= b[j].second; len -= 1ll * b[j].first * b[j].second; j++; } ans += b[i].second * cnt; ll tmp = len; for (int k = j - 1; k >= 1 && tmp <= sum; k--) { ans += rangecnt(sum - tmp - b[k].first - b[i].first, b[i].first, b[k].first, b[i].second - 1, b[k].second - 1); tmp += 1ll * b[k].first * b[k].second; } cnt += b[i].second; len += 1ll * b[i].first * b[i].second; } return ans; } int main() { read(n); for (int i = 1; i <= n; i++) read(a[i]); rmq :: init(); for (int i = 1; i <= n; i++) { int pos = i; while (pos <= n) { int g = rmq :: query(i, pos); int l = pos, r = n; while (l < r) { int mid = (l + r + 1) / 2; if (rmq :: query(i, mid) == g) l = mid; else r = mid - 1; } b[++tot] = make_pair(g, l - pos + 1); pos = l + 1; } } sort(b + 1, b + tot + 1); for (int i = 1; i <= tot; i++) if (b[i].first == b[m].first) b[m].second += b[i].second; else b[++m] = b[i]; ll l = 0, r = 1e16, rk = n * (n + 1ll) / 2; rk = rk * (rk + 1) / 2, rk = (rk + 1) / 2; while (l < r) { ll mid = (l + r) / 2; if (getcnt(mid) < rk) l = mid + 1; else r = mid; } writeln(l); return 0; }
**【F】**Ж-function
【思路要点】
- 我们用 表示原串后缀 的 ,对于询问 ,我们需要计算 。
- 考虑在后缀树上解决该问题,令后缀 对应的节点为 ,那么,对于询问 ,我们要对于每一个 的深度不超过 的祖先 ,计算 。需要注意的是这里的 可能不是一个后缀树中真实存在的节点。
- 考虑将上式拆分为 分别计算,注意这里 仍然代表 的深度不超过 的祖先。
- 第二部分比较容易,对后缀树树链剖分,将所有询问和修改按照 排序,扫描线 线段树即可。
- 对于第一部分,我们同样使用树链剖分,考虑单独处理每一条重链(及其上方的一条轻边)。
- 一个修改 会作用在该重链的一个前缀深度上,作用在深度 上时,会对同样作用在深度 上的,满足 的询问产生 的贡献;一个询问同样会作用在该重链的一个前缀深度上。
- 那么,我们将该重链上的询问和修改按照作用范围排序,同样适用扫描线来处理,对于一个询问,我们只需要分别考虑作用范围包含它的修改,和被它包含的修改即可,可以分别用树状数组维护,由于细节较为繁琐,在此不予讨论。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 4e5 + 5; const int MAXLOG = 20; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } vector <int> e[MAXN]; int n, oldn, m, depth[MAXN], home[MAXN]; int father[MAXN], size[MAXN], son[MAXN]; int timer, dfn[MAXN], rit[MAXN], up[MAXN]; int ql[MAXN], qr[MAXN], from[MAXN]; ll ans[MAXN]; int jump[MAXN][MAXLOG]; struct SegmentTree { struct Node { int lc, rc; int tag; ll cnt, sum; } a[MAXN * 2]; int n, root, size; void update(int root) { a[root].sum = a[a[root].lc].sum + a[a[root].rc].sum; } void build(int &root, int l, int r, int *tmp) { root = ++size; if (l == r) { a[root].cnt = tmp[l]; return; } int mid = (l + r) / 2; build(a[root].lc, l, mid, tmp); build(a[root].rc, mid + 1, r, tmp); a[root].cnt = a[a[root].lc].cnt + a[a[root].rc].cnt; } void init(int x, int *tmp) { n = x, root = size = 0; build(root, 1, n, tmp); } void pushdown(int root) { if (a[root].tag) { int tmp = a[root].lc, d = a[root].tag; a[tmp].tag += d; a[tmp].sum += d * a[tmp].cnt; tmp = a[root].rc; a[tmp].tag += d; a[tmp].sum += d * a[tmp].cnt; a[root].tag = 0; } } void modify(int root, int l, int r, int ql, int qr, int d) { if (l == ql && r == qr) { a[root].tag += d; a[root].sum += d * a[root].cnt; return; } pushdown(root); int mid = (l + r) / 2; if (mid >= ql) modify(a[root].lc, l, mid, ql, min(qr, mid), d); if (mid + 1 <= qr) modify(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, d); update(root); } void modify(int l, int r, int d) { modify(root, 1, n, l, r, d); } ll query(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].sum; pushdown(root); ll ans = 0; int mid = (l + r) / 2; if (mid >= ql) ans += query(a[root].lc, l, mid, ql, min(qr, mid)); if (mid + 1 <= qr) ans += query(a[root].rc, mid + 1, r, max(mid + 1, ql), qr); return ans; } ll query(int l, int r) { return query(root, 1, n, l, r); } int query(int root, int l, int r, int pos) { if (l == r) { return a[root].tag; } pushdown(root); int mid = (l + r) / 2; if (mid >= pos) return query(a[root].lc, l, mid, pos); else return query(a[root].rc, mid + 1, r, pos); } int query(int pos) { return query(root, 1, n, pos); } } ST; void ScanningLineI() { static int tmp[MAXN]; for (int i = 1; i <= n; i++) tmp[dfn[i]] = depth[i] - depth[father[i]]; ST.init(n, tmp); static vector <pair <pair <int, int>, int> > q[MAXN]; for (int i = 1; i <= m; i++) q[ql[i]].push_back(make_pair(make_pair(from[i], qr[i] - ql[i] + 1), i)); for (int i = 1; i <= oldn; i++) { function <ll (pair <int, int>) > query = [&] (pair <int, int> x) { ll ans = 1ll * (x.second - depth[father[x.first]]) * ST.query(dfn[x.first]); int pos = father[x.first]; while (pos) { ans += ST.query(dfn[up[pos]], dfn[pos]); pos = father[up[pos]]; } return ans; }; for (auto x : q[i]) ans[x.second] -= query(x.first); int pos = home[i]; while (pos) { ST.modify(dfn[up[pos]], dfn[pos], 1); pos = father[up[pos]]; } } } struct BinaryIndexTree { int n; ll a[MAXN]; void init(int x) { n = x; memset(a, 0, sizeof(a)); } void modify(int x, int d) { for (int i = x; i <= n; i += i & -i) a[i] += d; } ll query(int x) { ll ans = 0; for (int i = x; i >= 1; i -= i & -i) ans += a[i]; return ans; } ll query(int l, int r) { if (l > r) return 0; ll ans = 0; for (int i = r; i >= 1; i -= i & -i) ans += a[i]; for (int i = l - 1; i >= 1; i -= i & -i) ans -= a[i]; return ans; } } all[2], part[2]; void ScanningLineII() { static vector <pair <int, int> > mod[MAXN]; static vector <pair <pair <int, int>, int> > qry[MAXN]; for (int i = 1; i <= oldn; i++) { int pos = home[i]; while (pos) { mod[up[pos]].emplace_back(depth[pos], i); pos = father[up[pos]]; } } for (int i = 1; i <= m; i++) { int pos = from[i]; while (pos) { if (pos == from[i]) qry[up[pos]].emplace_back(make_pair(qr[i] - ql[i] + 1, qr[i] + 1), i); else qry[up[pos]].emplace_back(make_pair(depth[pos], qr[i] + 1), i); pos = father[up[pos]]; } } all[0].init(oldn * 2 + 2), all[1].init(oldn * 2 + 2); part[0].init(oldn * 2 + 2), part[1].init(oldn * 2 + 2); for (int i = 1; i <= n; i++) { if (up[i] != i) continue; int startdepth = depth[father[i]] + 1; sort(mod[i].begin(), mod[i].end()); reverse(mod[i].begin(), mod[i].end()); sort(qry[i].begin(), qry[i].end()); reverse(qry[i].begin(), qry[i].end()); vector <pair <int, int> > bak = mod[i]; for (auto x : bak) { all[0].modify(x.second, x.second); all[1].modify(x.second, 1); } while (!mod[i].empty() || !qry[i].empty()) { if (mod[i].empty() || (!qry[i].empty() && qry[i].back().first.first <= mod[i].back().first)) { pair <int, int> x = qry[i].back().first; int home = qry[i].back().second; ans[home] += part[1].query(x.second) * x.second + part[0].query(x.second); int tmp = x.second - x.first; if (tmp >= 1) ans[home] += all[1].query(1, tmp) * (x.first - startdepth + 1); chkmax(tmp, 0); ans[home] += all[1].query(tmp + 1, x.second - startdepth) * (x.second - startdepth + 1) - all[0].query(tmp + 1, x.second - startdepth); qry[i].pop_back(); } else { pair <int, int> x = mod[i].back(); all[0].modify(x.second, -x.second); all[1].modify(x.second, -1); part[0].modify(startdepth + x.second, -(startdepth + x.second - 1)); part[0].modify(x.first + 1 + x.second, startdepth + x.second - 1 + x.first - startdepth + 1); part[1].modify(startdepth + x.second, 1); part[1].modify(x.first + 1 + x.second, -1); mod[i].pop_back(); } } for (auto x : bak) { part[0].modify(startdepth + x.second, startdepth + x.second - 1); part[0].modify(x.first + 1 + x.second, -(startdepth + x.second - 1 + x.first - startdepth + 1)); part[1].modify(startdepth + x.second, -1); part[1].modify(x.first + 1 + x.second, 1); } } } void dfs(int pos, int fa) { size[pos] = 1; father[pos] = fa; for (auto x : e[pos]) { dfs(x, pos); size[pos] += size[x]; if (size[x] > size[son[pos]]) son[pos] = x; } } void efs(int pos, int fa, int from) { up[pos] = from; dfn[pos] = ++timer; if (son[pos]) efs(son[pos], pos, from); for (auto x : e[pos]) if (x != son[pos]) efs(x, pos, x); rit[pos] = timer; } struct SuffixAutomaton { struct Node { int child[26]; int depth, father, value; } a[MAXN]; char s[MAXN]; int root, size, last; int newnode(int depth) { a[++size].depth = depth; return size; } void extend(int ch, int pos) { int p = last, np = newnode(a[p].depth + 1); while (a[p].child[ch] == 0) { a[p].child[ch] = np; if (p != root) p = a[p].father; } if (a[p].child[ch] == np) a[np].father = root; else { int q = a[p].child[ch]; if (a[q].depth == a[p].depth + 1) a[np].father = q; else { int nq = newnode(a[p].depth + 1); memcpy(a[nq].child, a[q].child, sizeof(a[q].child)); a[nq].father = a[q].father; a[np].father = a[q].father = nq; while (a[p].child[ch] == q) { a[p].child[ch] = nq; if (p != root) p = a[p].father; } } } a[last = np].value = pos; } void init() { scanf("%s", s + 1); int len = strlen(s + 1); size = 0; root = last = newnode(0); for (int i = len; i >= 1; i--) extend(s[i] - 'a', i); oldn = len, n = size; for (int i = 1; i <= size; i++) { depth[i] = a[i].depth; if (a[i].value) home[a[i].value] = i; } for (int i = 2; i <= size; i++) e[a[i].father].push_back(i); } } SAM; int main() { SAM.init(); dfs(1, 0); efs(1, 0, 1); read(m); for (int i = 1; i <= m; i++) read(ql[i]), read(qr[i]); for (int i = 1; i <= n; i++) jump[i][0] = father[i]; for (int t = 1; t < MAXLOG; t++) for (int i = 1; i <= n; i++) jump[i][t] = jump[jump[i][t - 1]][t - 1]; for (int i = 1; i <= m; i++) { int pos = home[ql[i]], goal = qr[i] - ql[i] + 1; for (int j = MAXLOG - 1; j >= 0; j--) { int dest = jump[pos][j]; if (depth[dest] >= goal) pos = dest; } from[i] = pos; } ScanningLineI(); ScanningLineII(); for (int i = 1; i <= m; i++) writeln(ans[i]); return 0; }