这道题是考验如何处理一个区间的所有子区间的问题,我们可以将其变成区间操作。
线段树节点保存四个值,ls:表示这个区间从左起点开始的子区间的状态。rs:表示这个区间从右起点开始的子区间的状态,ts:表示这个区间中间的子区间的状态,s表示整段区间的状态。
维护的时候就是像平时区间维护一样。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100003
#define mid (l+r>>1)
#define lc (d<<1)
#define rc (d<<1|1)
struct Tr {
int ls, rs, ts, s;
}tr[N<<2];
int dp[1024][1024];
int root(int a) {
int tm, re = a;
while (re > 9) {
tm = re, re = 0;
while (tm) {
re += tm%10;
tm /= 10;
}
}
return re;
}
void init() {
int i, j, k, h;
for (i = 1;i < 1024;i++) {
for (j = 1;j < 1024;j++) {
dp[i][j] = 0;
for (k = 0;k < 10;k++) {
if (!(i&(1<<k))) continue;
for (h = 0;h < 10;h++) {
if (!(j&(1<<h))) continue;
dp[i][j] |= (1<<root(k+h));
}
}
}
}
}
void Push(int d) {
tr[d].s = dp[tr[lc].s][tr[rc].s];
tr[d].ls = tr[lc].ls|dp[tr[lc].s][tr[rc].ls];
tr[d].rs = tr[rc].rs|dp[tr[rc].s][tr[lc].rs];
tr[d].ts = tr[lc].ts|tr[rc].ts|dp[tr[lc].rs][tr[rc].ls];
}
void build(int d, int l, int r) {
if (l == r) {
scanf("%d", &tr[d].s);
tr[d].s = 1<<root(tr[d].s);
tr[d].ls = tr[d].rs = tr[d].ts = tr[d].s;
return;
}
build(lc, l, mid), build(rc, mid+1, r);
Push(d);
}
Tr query(int d, int l, int r, int L, int R) {
if (l == L && r == R) {
return tr[d];
}
if (R <= mid) return query(lc, l, mid, L, R);
else if (L > mid) return query(rc, mid+1, r, L, R);
else {
Tr tm, tl, tr;
tl = query(lc, l, mid, L, mid), tr = query(rc, mid+1, r, mid+1, R);
tm.s = dp[tl.s][tr.s];
tm.ls = tl.ls|dp[tl.s][tr.ls];
tm.rs = tr.rs|dp[tr.s][tl.rs];
tm.ts = tl.ts|tr.ts|dp[tl.rs][tr.ls];
return tm;
}
}
int main() {
int T, n, m, l, r, ca = 1, i, j;
scanf("%d", &T);
init();
while (T--) {
if (ca != 1) puts("");
scanf("%d", &n);
build(1, 1, n);
scanf("%d", &m);
printf("Case #%d:\n", ca++);
while (m--) {
scanf("%d%d", &l, &r);
Tr ans = query(1, 1, n, l, r);
int st = ans.ls|ans.rs|ans.ts;
j = 0;
for (i = 9;i >= 0 && j < 5;i--) {
if (st&(1<<i)) {
if (j) printf(" ");
printf("%d", i);
j++;
}
}
while (j < 5) {
if (j) printf(" ");
printf("-1");
j++;
}
puts("");
}
}
}