P3588 PUS 题解
PUS
推销我的洛谷博客。
题意
给出三个整数
如果存在一个满足要求的数组 TAK
,然后在下一行输出任意一种满足要求的 NIE
。
数据范围
。 。 。 。
思路
线段树优化建图。
初步思路:将约束条件转为建图
题目要求构造一个合法的
那么我们可以根据约束条件,建出一张由较大元素连向较小元素的有向图,最后根据这张图即可构造
如果是暴力建图,我们需要将每个
建图的优化:集中点
如果做过 abc270_f Transportation 的话,你可以想到对于每个约束条件都建一个集中节点
别着急,这都是为了为下一步优化做铺垫。
注意有一个细节,题目中约束条件是严格大于,所以
建图再次优化:线段树优化建图
可以发现,
由于题目保证
附:线段树优化建图的模板。
最终:整理答案和判断无解
这个很简单,使用拓扑排序即可解决,注意上面所说的细节。
无解情况如下:
-
在最优秀的构造方案中,你的
肯定是尽量越大越好,所以当你发现某个 ,那么必然是无解的。 -
如果你发现最初已经给定了某个元素
并且 ,那么也是不合法的。 -
如果出现了环,那么也是不合法的。
那么本题就结束了,还不理解就看代码。
复杂度
- 时间:
。 - 空间:
。
Code
点击查看代码
#include <bits/stdc++.h> using namespace std; using ll = long long; const int N = 1e5 + 10, M = 2e5; // ls 和 rs 用于记录线段树左右儿子,a 用于记录方案,b 是上文所说的 x,cnb 用于拓扑排序,mi 见上文 int n, m, s, rt, ncnt, ls[N * 3 + M], rs[N * 3 + M], a[3 * N + M], b[N], cnb[3 * N + M], mi[3 * N + M]; vector<int> g[N * 3 + M]; queue<int> q; //----------------------------------------------------------------- 线段树优化建图 void Add_edge (int x, int y) { g[y].push_back(x), cnb[x]++; } void build (int id, int l, int r) { if (l == r) { ls[id] = l; Add_edge(l, id); return ; } int mid = (l + r) >> 1; ls[id] = ++ncnt, rs[id] = ++ncnt; build(ls[id], l, mid), build(rs[id], mid + 1, r); Add_edge(ls[id], id), Add_edge(rs[id], id); } void modify (int id, int l, int r, int x, int y, int u) { if (x <= l && r <= y) { Add_edge(id, u); return ; } if (l > y || r < x) return ; int mid = (l + r) >> 1; modify(ls[id], l, mid, x, y, u), modify(rs[id], mid + 1, r, x, y, u); } //----------------------------------------------------------------- 线段树优化建图 int main () { ios::sync_with_stdio(0), cin.tie(0); cin >> n >> s >> m, ncnt = n; rt = ++ncnt, build(rt, 1, n); for (int i = 1, x, y; i <= s; i++) cin >> x >> y, a[x] = y; // 最初给定的元素直接赋值即可 for (int x, k; m--; ) { cin >> b[0] >> x >> k, ncnt++, b[0]--; for (int i = 1; i <= k; i++) { cin >> b[i], g[b[i]].push_back(ncnt), cnb[ncnt]++; if (b[i] != b[i - 1] + 1) //只用管相邻两个元素之间的区间 modify(rt, 1, n, b[i - 1] + 1, b[i] - 1, ncnt); } if (x != b[k]) // 最后一个区间别漏了 modify(rt, 1, n, b[k] + 1, x, ncnt); } for (int i = 1; i <= ncnt; i++) // 初始化为 1e9 + 1 是为了方便下方减一 mi[i] = 1e9 + 1; q.push(rt); while (q.size()) { int x = q.front(); q.pop(); if (a[x]) {// 初始时有数 if (mi[x] - 1 < a[x]) { // 判断无解情况 #2 cout << "NIE"; return 0; } } else { a[x] = mi[x] - (x <= n); // 细节:集中点 a[i] = mi[i] if (a[x] <= 0) { // 判断无解情况 #1 cout << "NIE"; return 0; } } for (int i : g[x]) { // 拓扑排序都会写吧 mi[i] = min(mi[i], a[x]), cnb[i]--; if (!cnb[i]) q.push(i); } } for (int i = 1; i <= n; i++) if (a[i] <= 0) { // 如果有环的话,肯定有元素没被赋值。判断无解情况 #1 cout << "NIE"; return 0; } cout << "TAK\n"; for (int i = 1; i <= n; i++) cout << a[i] << ' '; return 0; }
本文作者:wnsyou
本文链接:https://www.cnblogs.com/wnsyou-blog/p/18313027/P3588_solution
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步