\(\mathscr{Description}\)

  Link.

  给定 \(n\) 盏灯, 你可以选择让第 \(i\) 盏照亮 \([i-p_i,i)\) 或者 \((i,i+p_i]\), 构造方案覆盖 \([1,n]\) 内的整点, 或声明无解.

  多测, \(\sum n\le3\times10^5\).

\(\mathscr{Solution}\)

  某模拟赛后得知这是典题, 那兔是脱离时代了. 加之最近好像更博客比较咕 (你以为是在写 Tower? 当然不是! 就是咕, 咕咕咕呱), 所以写写单题题解吧.

  这看上去是一个判定性问题, 但 01 状态显然损失了信息量. 所以无论是区间 DP 还是什么序列 DP, 我们设计 DP 状态的思路都是 “最大/最小化某个东西, 尽可能让子问题有解”.

  比如我们有这么一个设计: 令 \(f(i)\) 表示考虑了灯 \([1,i]\), 最多能完全照亮 \([1,f(i)]\) 内的整点. 诸如此类的状态似乎都有转移后效性, 这就是本题的难点.

  去掉后效性? 贪心地整体决策! 对于上面这个状态, 如果 \(i\) 选择向前覆盖, 而若覆盖到一个已有 \(f(j)\ge j\)\(j\), 我们自然让 \((j,i)\) 内的灯全部向右照. 若此时 \(f(j)\) 的实际决策还能增大 \(f(i)\) 的理想值 … 那 \(i\) 摆烂得了, 不需要我们来考虑转移.

  意识流地讨论之后, 我们来具体分析转移.

  • \(i\) 摆烂. \(f(i)\overset{\max}{\longleftarrow}f(i-1)\);
  • \(i\) 往右照, 要求 \(i\) 自己被照到, 不然相当于 \(i\) 摆烂. \(f(i)\overset{\max}{\longleftarrow}\max\{f(i-1),i+p_i\}\), 其中 \(f(i-1)\ge i\).
  • \(i\) 往右照, 这是刚刚糊过的难点. \(f(i)\overset{\max}{\longleftarrow}\max\{\{k+p_k\mid k\in(j,i)\}\cup\{i-1\}\}\), 其中 \(j\) 满足 \(f(j)\ge i-p_i-1\).

  (这里其实我觉得没必要分自然段, 但是我怕有人觉得我忘记分段了所以还是分一下段. 欸你写题解屁话真的多.) 需要优化一下第三种转移, 显然最小的 \(j\) 将带来最优质转移. std::lower_bound\(j\), 挂上 BIT 在线求 RMQ 即可. 复杂度 \(\mathcal O(n\log n)\). 听闻 “精细实现” 可以 \(\mathcal O(n)\) awa!

\(\mathscr{Code}\)

/*+Rainybunny+*/

#include <bits/stdc++.h>

#define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
#define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)

typedef std::pair<int, int> PII;
#define fi first
#define se second

inline char fgc() {
    static char buf[1 << 17], *p = buf, *q = buf;
    return p == q && (q = buf + fread(p = buf, 1, 1 << 17, stdin), p == q) ?
      EOF : *p++;
}

template <typename Tp = int>
inline Tp rint() {
    Tp x = 0, s = fgc(), f = 1;
    for (; s < '0' || '9' < s; s = fgc()) f = s == '-' ? -f : f;
    for (; '0' <= s && s <= '9'; s = fgc()) x = x * 10 + (s ^ '0');
    return x * f;
}

template <typename Tp>
inline void wint(Tp x) {
    if (x < 0) putchar('-'), x = -x;
    if (9 < x) wint(x / 10);
    putchar(x % 10 ^ '0');
}

const int MAXN = 3e5;
int n, p[MAXN + 5], f[MAXN + 5], pre[MAXN + 5];
char ans[MAXN + 5];

struct BIT {
    PII val[MAXN + 5];
    inline void modify(int x, const PII& k) {
        for (; x <= n; x += x & -x) val[x] = std::max(val[x], k);
    }
    inline PII query(int x) const {
        PII ret(0, 0);
        for (; x; x ^= x & -x) ret = std::max(ret, val[x]);
        return ret;
    }
} bit;

int main() {
    for (int T = rint(); T--;) {
        n = rint();
        rep (i, 1, n) p[i] = rint();

        memset(bit.val + 1, 0, sizeof(bit.val[1]) * n);
        bit.modify(n, { 1 + p[1], 1 });
        rep (i, 2, n) {
            f[i] = f[i - 1], pre[i] = -1;
            if (f[i - 1] >= i) f[i] = std::max(f[i - 1], i + p[i]);
            int idx = std::lower_bound(f, f + i, i - p[i] - 1) - f;
            PII t = bit.query(n - idx);
            if (idx < i && f[i] < std::max(i - 1, t.fi)) {
                f[i] = std::max(i - 1, t.fi), pre[i] = idx;
            }
            bit.modify(n - i + 1, { i + p[i], i });
            // printf("f(%d)=%d\n", i, f[i]);
        }

        if (f[n] < n) puts("NO");
        else {
            puts("YES"), ans[n + 1] = '\0';
            for (int p = n; p;) {
                // assert(~pre[p] && f[p] >= p);
                if (!~pre[p]) ans[p] = 'R', --p;
                else {
                    int t = pre[p]; ans[p] = 'L';
                    while (--p > t) ans[p] = 'R';
                }
            }
            puts(ans + 1);
        }
    }
    return 0;
}

原文地址:http://www.cnblogs.com/rainybunny/p/16864050.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性