【HDU 4351】Digital root【线段树】

13 篇文章 0 订阅
7 篇文章 0 订阅

          这道题是考验如何处理一个区间的所有子区间的问题,我们可以将其变成区间操作。

          线段树节点保存四个值,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("");
        }
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值