[ROI 2019] 拍照 题解

前言

题目链接:洛谷

题意简述

给定最终序列 \(a_n\),找出一种方案,每次指定 \(c,l,r\) 将区间 \([l,r]\) 染成 \(c\),满足每个 \(c\) 互不相同。找出一种合法方案或报告无解。

\(n\leq3\times10^5\)

题目分析

显然 \(L,R\) 就是 \(a\) 中该颜色最左最右的位置,剩下只需要考虑顺序。考虑建图,若要染 \(i\) 必须先染 \(j\),那么连边 \(j\rightarrow i\),最后拓扑排序即可,无解为有环。什么时候必须先染 \(j\)?显然为 \(\exists x\in[L_j,R_j],a_x=i\)。考虑为每个位置 \(x\) 建立新点 \(n+x\),连边 \(n+x\rightarrow a_x\)。那么对于颜色 \(i\),对于每两个相邻的 \(a_l=a_r=i\),让 \(\forall p\in(l,r)\) 连边 \(j\rightarrow n+p\) 即可完成建图。显然线段树优化建图。时间复杂度线性对数。

代码

边倒着连,是浮水法的思想。最后附了一个 checker。

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int N = 3e5 + 10;

int m, n;
int a[N];

struct {
    int c, l, r;
} ans[N];
int ac;

namespace yzh {

const int _N = N + N + N * 2      + N;
const int _M = N * 2 + N + 36 * N + N;

struct {
   int v, nxt;
} e[_M];
int eid = 1, head[_N], du[_N];
inline void add(int u, int v) {
    ++du[v];
    e[++eid] = { v, head[u] };
    head[u] = eid;
}

#define ls (u << 1)
#define rs (u << 1 | 1)
int tot, p[N << 2];
void build(int u, int l, int r) {
    if (l == r) {
        p[u] = n + l;
        return;
    }
    p[u] = ++tot;
    int mid = (l + r) >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
    add(p[ls], p[u]);
    add(p[rs], p[u]);
}
void add(int u, int trl, int trr, int l, int r, int x) {
    if (l <= trl && trr <= r) return add(p[u], x);
    int mid = (trl + trr) >> 1;
    if (l <= mid) add(ls, trl, mid, l, r, x);
    if (r > mid) add(rs, mid + 1, trr, l, r, x);
}
#undef ls
#undef rs

vector<int> vec[N];
int Q[_N];
void solve() {
    for (int i = 1; i <= m; ++i) {
        vec[a[i]].emplace_back(i);
    }
    tot = n + m;
    build(1, 1, m);
    for (int i = 1; i <= n; ++i) {
        if (vec[i].empty()) {
            continue;
        }
        for (int x : vec[i]) {
            add(i, n + x);
        }
        int sz = vec[i].size();
        for (int j = 1; j < sz; ++j) {
            int l = vec[i][j - 1], r = vec[i][j];
            if (l + 1 <= r - 1) {
                add(1, 1, m, l + 1, r - 1, i);
            }
        }
    }
    int top = 0;
    for (int i = 1; i <= tot; ++i)
        if (!du[i]) {
            Q[++top] = i;
        }
    while (top) {
        int u = Q[top--];
        if (u <= n && !vec[u].empty()) {
            int l = vec[u].front();
            int r = vec[u].back();
            ans[++ac] = { u, l, r };
        }
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].v;
            if (!--du[v]) {
                Q[++top] = v;
            }
        }
    }
    bool ok = true;
    for (int i = 1; i <= tot; ++i)
        if (du[i]) {
            ok = false;
            break;
        }
    if (!ok)
        puts("-1");
    else {
        reverse(ans + 1, ans + ac + 1);
        printf("%d\n", ac);
        for (int i = 1; i <= ac; ++i) {
            printf("%d %d %d\n", ans[i].c, ans[i].l, ans[i].r);
        }
    }
}

}

int main() {
    scanf("%d%d", &m, &n);
    for (int i = 1; i <= m; ++i) {
        scanf("%d", &a[i]);
    }
    yzh::solve();
    return 0;
}

#if 0
#include <cstdio>
#include <iostream>
#include <vector>
#include <set>
using namespace std;

const int N = 3e5 + 10;

int m, n, seq[N];
int k1, k2;

vector<pair<int, int>> ins[N], del[N];
set<pair<int, int>> st;

void ERR(const char *msg) {
    fputs("WA\n", stderr);
    fprintf(stderr, "reason: %s\n", msg);
    exit(1);
}

void check(FILE* out, int k) {
    for (int i = 1; i <= m; ++i) {
        ins[i].clear();
        del[i].clear();
    }
    for (int i = 1, c, l, r; i <= k; ++i) {
        fscanf(out, "%d%d%d", &c, &l, &r);
        ins[l].emplace_back(i, c);
        del[r + 1].emplace_back(i, c);
    }
    st.clear();
    for (int i = 1; i <= m; ++i) {
        for (auto x : ins[i]) st.insert(x);
        for (auto x : del[i]) st.erase(x);
        if (st.empty()) ERR((string("st is empty at pos ") + to_string(i)).c_str());
        int o = st.rbegin()->second;
        if (o != seq[i]) ERR((string("differ at pos ") + to_string(i)).c_str());
    }
}

int main() {
    FILE* in = fopen("PT.in", "r");
    FILE* ans = fopen("xym", "r");
    FILE* out = fopen("yzh", "r");
    fscanf(in, "%d%d", &m, &n);
    for (int i = 1; i <= m; ++i) {
        fscanf(in, "%d", &seq[i]);
    }
    fscanf(ans, "%d", &k1);
    fscanf(out, "%d", &k2);
    if (k1 == -1 && k2 == -1) {
        puts("AC no solution.");
        exit(0);
    }
    if (k1 == -1 || k2 == -1) {
        ERR("no sol but give a sol");
    }
    check(ans, k1);
    check(out, k2);
    puts("AC");
    exit(0);
}

#endif
posted @ 2025-08-29 15:06  XuYueming  阅读(25)  评论(0)    收藏  举报