HDU-6107 Typesetting(倍增法)

本文介绍使用ST算法解决一种特殊的文本布局问题。通过预处理技术构造数据结构,实现快速查询任意长度段落内的最大单词数量。适用于两种场景:纯文字段落与包含图片的段落。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门:HDU-6107

题解:ST算法

整片文章可以被分成2部分:①中间有图片的部分;②中间有图片的部分

可以将这2部分分别用ST离线,f[i][j]表示以第i个单词开始,连续1<<j行能写多少单词。要注意的是:在第②部分中,如果第一个单词的宽度大于dw和w-pw-dw的话,那么这一段f[i][j]=0

#include<bits/stdc++.h>
#define FIN freopen("in.txt","r",stdin);
using namespace std;
typedef long long LL;
const int MX = 1e5 + 5;
const int inf = 0x3f3f3f3f;
int mx, n, w, dw, pw, a[MX];
int f1[MX][30]; //从第i个开始,占用1<<j行能容下的字符
int f2[MX][30];
void ST() {
    for (int i = 1; i <= n; i++) {
        int cnt = a[i], j = i + 1;
        while (cnt + a[j] + 1 <= w && j <= n) cnt += a[j++] + 1;
        f1[i][0] = j - i;
    }
    for (int j = 1; (1 << j) <= mx; j++) {
        for (int i = 1; i <= n; i++) {
            f1[i][j] = f1[i][j - 1] + f1[i + f1[i][j - 1]][j - 1];
        }
    }
    for (int i = 1; i <= n; i++) {
        int cnt = 0, j = i, flag = 0;
        while (cnt + a[j] + flag <= dw) cnt += a[j++] + flag, flag = 1;
        int k = j;
        cnt = flag = 0;
        while (cnt + a[k] + flag <= w - pw - dw) cnt += a[k++] + flag, flag = 1;
        f2[i][0] = k - i;
    }
    for (int j = 1; (1 << j) <= mx; j++) {
        for (int i = 1; i <= n; i++) {
            if (f2[i][j - 1] == 0) f2[i][j] = 0;
            else f2[i][j] = f2[i][j - 1] + f2[i + f2[i][j - 1]][j - 1];
        }
    }
}
int RMQ1(int i, int x) {
    if (x == 0) return i;
    while (x && i <= n) {
        int j = 0;
        while ((1 << (j + 1)) <= x) j++;
        i += f1[i][j];
        x -= (1 << j);
    }
    return i;
}
int RMQ2(int i, int x) {
    if (x == 0) return i;
    while (x && i <= n) {
        int j = 0;
        while ((1 << (j + 1)) <= x) j++;
        i += f2[i][j];
        x -= (1 << j);
    }
    return i;
}
int RMQ3(int i) {
    int ret = 0;
    while (i <= n) {
        int j = 0;
        while (i + f1[i][j + 1] <= n) j++;
        i += f1[i][j];
        ret += (1 << j);
    }
    return ret;
}

struct Query {
    int x, h;
} q[MX];
int main() {
    //FIN;
    int T, m;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d%d%d", &n, &w, &pw, &dw);
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
        scanf("%d", &m);
        mx = n;
        for (int i = 1; i <= m; i++) scanf("%d%d", &q[i].x, &q[i].h), mx = max(mx, n + q[i].x + q[i].h);
        ST();
        for (int i = 1; i <= m; i++) {
            int x = q[i].x, h = q[i].h;
            int tmp = RMQ3(1);
            if (tmp <= x - 1) {
                printf("%d\n", tmp + h);
                continue;
            }
            int ans = x + h - 1;
            int p = RMQ1(1, x - 1);
            p = RMQ2(p, h);
            if (p <= n) ans += RMQ3(p);
            printf("%d\n", ans);
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值