123789456ye

已AFO

[POI2015]PUS

题面:Luogu
题解:线段树优化建图+差分约束+toposort
可以发现\([l,r]\)区间被分成了\(k+1\)段区间
然后发现这是典型的差分约束,从区间向单点连边
大概就是建一颗出树
每一次区间连边,就把这些区间对应的点连向一个新建的虚拟节点,然后再从这个节点连向对应位置
具体看代码


我也不知道为什么这道题要上拓扑排序,按理来讲应该可以全连向虚拟节点然后跑spfa啊?
大概是因为边权为0/1?
求最长路可以在拓扑排序中完成

#include<bits/stdc++.h>
using namespace std;
inline void read(int& x)
{
	x = 0; char c = getchar(); int f = 1;
	while (!isdigit(c)) { if (c == '-') f = -1; c = getchar(); }
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
	x *= f;
}
#define maxn 1000005
const int lim = 1e9;
struct Edge
{
	int fr, to, val;
}eg[maxn << 1];
int head[maxn], edgenum, deg[maxn];
inline void add(int fr, int to, int val)
{
	eg[++edgenum] = { head[fr],to,val };
	head[fr] = edgenum; ++deg[to];
}
int n, s, m;
int root[maxn], tot;
#define ls rt<<1
#define rs rt<<1|1
void build(int rt, int l, int r)
{
	if (l == r) { root[rt] = l; return; }
	int mid = (l + r) >> 1;
	root[rt] = ++tot;
	build(ls, l, mid), build(rs, mid + 1, r);
	add(root[ls], root[rt], 0), add(root[rs], root[rt], 0);
}
void link(int rt, int l, int r, int fr, int to, int To)
{
	if (fr <= l && to >= r) return add(root[rt], To, 0);
	int mid = (l + r) >> 1;
	if (fr <= mid) link(ls, l, mid, fr, to, To);
	if (to > mid) link(rs, mid + 1, r, fr, to, To);
}
int dis[maxn], a[maxn], vis[maxn];
#define to eg[i].to
void Toposort()
{
	queue<int> q;
	for (int i = 1; i <= tot; ++i)
	{
		if (!deg[i]) q.push(i);
		if (!dis[i]) dis[i] = 1;//如果这一点没有值,就初始化为1
	}
	while (!q.empty())
	{
		int tp = q.front(); q.pop(); vis[tp] = 1;
		for (int i = head[tp]; i; i = eg[i].fr)
		{
			dis[to] = max(dis[to], dis[tp] + eg[i].val);
			if (a[to] && dis[to] > a[to]) puts("NIE"), exit(0);
			if (!--deg[to]) q.push(to);
		}
	}
}
int main()
{
	int n, s, m;
	read(n), read(s), read(m), tot = n;
	build(1, 1, n);
	for (int i = 1, pos, val; i <= s; ++i)
		read(pos), read(val), a[pos] = dis[pos] = val;
	for (int i = 1, l, r, k, las; i <= m; ++i)
	{
		read(l), read(r), read(k);
		las = l - 1, ++tot;
		for (int j = 1, tp; j <= k; ++j)
		{
			read(tp);
			add(tot, tp, 1);//tp向这个区间连边,表示tp比这些至少大1(严格大于)
			if (tp > las + 1) link(1, 1, n, las + 1, tp - 1, tot);
			las = tp;
		}
		if (las < r) link(1, 1, n, las + 1, r, tot);
	}
	Toposort();
	for (int i = 1; i <= tot; ++i)
		if (!vis[i] || dis[i] > lim) puts("NIE"), exit(0);//注意lim的限制
	puts("TAK");
	for (int i = 1; i <= n; ++i) printf("%d ", dis[i]);
	return 0;
}
posted @ 2020-03-30 17:42  123789456ye  阅读(134)  评论(0编辑  收藏  举报