bzoj4383 [POI2015]Pustynia 拓扑排序+差分约束+线段树优化建图

题目传送门

https://lydsy.com/JudgeOnline/problem.php?id=4383

题解

暴力的做法显然是把所有的条件拆分以后暴力建一条有向边表示小于关系。

因为不存在零环,所以随后可以用拓扑排序来解决。


考虑如何优化。

第一个优化比较显而易见,对于一个条件,新建一个虚点,连向所有给出的点。

第二个是,我们可以发现,给定这 \(k\) 个点以后,实际上需要建的边是从最多 \(k + 1\) 个区间中引出的,所以可以考虑线段树优化建图。

然后就可以和上面一样的,因为不存在零环,所以随后可以用拓扑排序来解决。

然后这样的时间复杂度是 \(O((m+\sum k)\log n)\)


这似乎是我第一次写线段树优化建图呢~虽然一年前就已经学过了,但是一直没写过。

#include<bits/stdc++.h>
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back
template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}
typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
template<typename I> inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
const int N = 100000 * 6 + 7;
const int M = 100000 * (4 + 3 + 17 + 17) + 7;
#define lc o << 1
#define rc o << 1 | 1
int n, ns, m, nod;
int dp[N], nd[N], idg[N], pre[N], rel[N], tmp[N], q[N];
struct Edge { int to, ne, w; } g[M]; int head[N], tot;
inline void addedge(int x, int y, int z) { g[++tot].to = y, g[tot].w = z, g[tot].ne = head[x], head[x] = tot; }
inline void adde(int x, int y, int z) { addedge(x, y, z), addedge(y, x, z); }
inline void add_e(int x, int y, int z) { ++idg[y], addedge(x, y, z); }
inline void build(int o, int L, int R) {
if (L == R) return (void)(rel[L] = o, pre[o] = L);
int M = (L + R) >> 1;
add_e(lc, o, 0), build(lc, L, M);
add_e(rc, o, 0), build(rc, M + 1, R);
}
inline void addi(int o, int L, int R, int l, int r, int x) {
// dbg("o = %d, L = %d, R = %d, l = %d, r = %d, x = %d\n", o, L, R, l, r, x);
if (l <= L && R <= r) return add_e(o, x, 1);
int M = (L + R) >> 1;
if (l <= M) addi(lc, L, M, l, r, x);
if (r > M) addi(rc, M + 1, R, l, r, x);
}
inline void work() {
int hd = 0, tl = 0;
for (int i = 1; i <= nod; ++i) if (idg[i] == 0) q[++tl] = i, smax(dp[i], 1);
while (hd < tl) {
int x = q[++hd];
// if (dp[x] >= n) dbg("******* x = %d, dp[x] = %d, nd[x] = %d\n", x, dp[x], nd[x]);
if (nd[x]) {
if (dp[x] > nd[x]) {
puts("NIE");
return;
} else dp[x] = nd[x];
}
for fec(i, x, y) {
smax(dp[y], dp[x] + g[i].w);
if (!--idg[y]) q[++tl] = y;
}
}
for (int i = 1; i <= n; ++i) if (!dp[rel[i]] || dp[rel[i]] > 1e9) {
puts("NIE");
return;
}
puts("TAK");
for (int i = 1; i <= n; ++i) printf("%d%c", dp[rel[i]], " \n"[i == n]);
}
inline void init() {
read(n), read(ns), read(m);
int x, y;
build(1, 1, n);
nod = n << 2;
for (int i = 1; i <= ns; ++i) read(x), read(y), dp[rel[x]] = nd[rel[x]] = y;
for (int i = 1; i <= m; ++i) {
int l, r, k;
read(l), read(r), read(k);
++nod;
for (int i = 1; i <= k; ++i) {
read(tmp[i]);
add_e(nod, rel[tmp[i]], 0);
if (i == 1) tmp[i] > l ? addi(1, 1, n, l, tmp[i] - 1, nod) : (void)0;
else tmp[i] - tmp[i - 1] > 1 ? addi(1, 1, n, tmp[i - 1] + 1, tmp[i] - 1, nod) : (void)0;
}
if (tmp[k] != r) addi(1, 1, n, tmp[k] + 1, r, nod);
}
}
int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}
posted @   hankeke303  阅读(168)  评论(0编辑  收藏  举报
编辑推荐:
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
阅读排行:
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· DeepSeek火爆全网,官网宕机?本地部署一个随便玩「LLM探索」
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 上周热点回顾(1.20-1.26)
· 【译】.NET 升级助手现在支持升级到集中式包管理
历史上的今天:
2018-10-31 [校内NOIP2017模拟20171029] 不可逆的重启动
2018-10-31 [校内NOIP2018模拟20181031] 西行妖下
2018-10-31 [校内NOIP2018模拟20181031] 式神守护
2018-10-31 [校内NOIP2018模拟20181031] 白玉楼前
点击右上角即可分享
微信分享提示