[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
本文作者:XuYueming,转载请注明原文链接:https://www.cnblogs.com/XuYueming/p/19064564。
若未作特殊说明,本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。

浙公网安备 33010602011771号