洛谷P3588 PUS 线段树优化建图

网址:https://www.luogu.com.cn/problem/P3588

题意:

给一个长度是$n$的正整数序列,范围$[1,1e9]$,给出了其中的$s$个数和$m$条信息,每条信息包含$l,r,k$和$k$个数,表示$a_l,a_l+1......a_r-1,a_r$里这$k$个数任意一个都比剩下的$r-l+1-k$个数严格大。构造一个合法的序列或者判断无解。

题解:

我们把严格大定义成有向图中从起点到终点的一条有向边,但是现在是一段连向一段,并且序列的长度达到$1e5$,所以普通方法建图,空间大小达不到要求。所以我们用线段树优化建图。把这个序列建成一棵线段树,然后对于每一个区间$[l,r]$,可以分割成$k+1$个小区间(区间内可以没有元素),然后因为这$r-l+1-k$个点都小于给出的$k$个点,则需要连接$k*(r-l+k-1)$条边,我们可以考虑连接超级结点,再连接到那$k$个点上。这样子每一次需要连接$r-l+1$条边,这样子仍然不能通过题目。考虑使用线段树,就只需要建立$k+log(r-l+1)$条边,线段树上有$nlogn$条边,总边数是$(\sum k)+klogn+nlogn$条边,可以通过。

对于每个提问,连向$k+1$个点连向超级结点的边边权是$1$,超级结点连向这$k$个点的边边权是$0$。为什么不能反过来呢?其实可以。然后线段树上显然是子节点向父节点连一条权值为$0$的边,连完边之后拓扑排序即可,如果发现整数的值大于$1e9$或者有环,则无解。

AC代码:

#include 
using namespace std;
const int MAXN = (1e6 + 5);
struct node
{
	int l, r;
};
node tr[MAXN];
struct edge
{
	int to, w;
	edge() {}
	edge(int _to, int _w) :to(_to), w(_w) {}
	bool operator<(const edge& a)const
	{
		return w > a.w;
	}
};
vectorG[MAXN];
int cnt;
int deg[MAXN];
void build(int& rt, int l, int r)
{
	if (l == r)
	{
		rt = l;
		return;
	}
	rt = ++cnt;
	int m = (l + r) >> 1;
	build(tr[rt].l, l, m);
	build(tr[rt].r, m + 1, r);
	G[tr[rt].l].push_back(edge(rt, 0));
	G[tr[rt].r].push_back(edge(rt, 0));
	deg[rt] += 2;
}
void update(int rt, int l, int r, int ql, int qr, int v)
{
	if (l > r)
		return;
	if (l <= ql && r >= qr)
	{
		G[rt].push_back(edge(v, 1));
		++deg[v];
		return;
	}
	int m = (ql + qr) >> 1;
	if (l <= m)
		update(tr[rt].l, l, r, ql, m, v);
	if (r > m)
		update(tr[rt].r, l, r, m + 1, qr, v);
}
queueQ;
long long dis[MAXN];
int val[MAXN];
bool vis[MAXN];
bool toposort()
{
	for (int i = 1; i <= cnt; ++i)
		if (!deg[i])
		{
			Q.push(i);
			dis[i] = val[i] ? val[i] : 1;
		}
	while (Q.size())
	{
		int u = Q.front();
		Q.pop();
		vis[u] = 1;
		for (auto i : G[u])
		{
			int v = i.to, w = i.w;
			--deg[v];
			dis[v] = max(dis[v], dis[u] + w);
			if (!vis[v] && !deg[v])
			{
				if (val[v])
				{
					if (dis[v] > val[v])
						return false;
					dis[v] = val[v];
				}
				Q.push(v);
			}
		}
	}
	for (int i = 1; i <= cnt; ++i)
		if (!vis[i] || dis[i] > 1e9)
			return false;
	return true;
}
int rt;
int main()
{
	int n, s, m;
	scanf("%d%d%d", &n, &s, &m);
	cnt = n;
	int a, p;
	while (s--)
	{
		scanf("%d%d", &a, &p);
		val[a] = p;
	}
	int las, x, k, l, r;
	build(rt, 1, n);
	while (m--)
	{
		scanf("%d%d%d", &l, &r, &k);
		las = l;
		++cnt;
		while (k--)
		{
			scanf("%d", &x);
			update(rt, las, x - 1, 1, n, cnt);
			G[cnt].push_back(edge(x, 0));
			++deg[x];
			las = x + 1;
		}
		update(rt, las, r, 1, n, cnt);
	}
	if (!toposort())
		printf("NIE\n");
	else
	{
		printf("TAK\n");
		for (int i = 1; i <= n; ++i)
			printf("%lld%c", dis[i], i == n ? '\n' : ' ');
	}
	return 0;
}	

 

你可能感兴趣的:(洛谷P3588 PUS 线段树优化建图)