[BZOJ4383][POI2015]Pustynia (拓扑排序)

题意:给定一个长度为n的正整数序列a,每个数都在1到10^9范围内,告诉你其中s个数,并给出m条信息,每条信息包含三个数l,r,k以及接下来k个正整数,表示a[l]…a[r]里这k个数中的任意一个都比任意一个剩下的r-l+1-k个数大。任意构造出一组满足条件的方案,或者判断无解。n<=10w, m<=20w, k的和<=30w。

定义一条有向边(u,v,w)表示a[u]-w>=a[v],对于每条信息,枚举属于那k个数中的某个数i向每个不在那k个数当中的数连一条权值为1的边。跑拓扑排序DP,如果无环且合法则输出即可。但是这样连边边数是n^2的,会爆。

考虑vfk的a+b problem那套理论,由于连边的一定是一段连续的区间,我们用线段树优化构图。一开始线段树上父亲节点向儿子节点连权值为0的边,然后每次得到一条信息,抽象一个虚拟点,将K个点全部向这个虚拟点连权值为0的边。这K个点会将这个区间分成K+1段连续区间,我们对每一段连续区间在线段树上查询到logn段区间,然后虚拟点像这logn段区间连边即可。总共连的边数应该是O(n+Klogn)条,点数为O(n+m)。然后就可以跑拓扑排序了。实现的时候注意有很多细节,比如每个数的取值范围之类的。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<assert.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define erp(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
const int MAXN = 100005;
int mxi = 1000000000;
const int MAXV = MAXN * 6;
inline void gmin(int&a, const int&b) { a>b?a=b:0; }
inline void gmax(int&a, const int&b) { a<b?a=b:0; }
inline int idx(int l, int r) { return (l+r) | (l!=r); }

int N, S, M, ax[MAXV], a[MAXV], ind[MAXV];
bool vis[MAXV];
void noso() { puts("NIE"); exit(0); }

inline void get(int&r)
{
	char c, f=0; r=0;
	do {c=getchar();if(c=='-') f=1;} while(c<'0'||c>'9');
	do r=r*10+c-'0', c=getchar(); while(c>='0'&&c<='9');
	if (f) r = -r;
}

struct Ed { int to, d, nxt; } e[MAXN*40];
int adj[MAXV], ec, nodecnt, vn;
void adde(int a, int b, int c) // b <= a-c
{
	e[++ec].to = b;
	e[ec].d = c;
	e[ec].nxt = adj[a];
	ind[b] ++;
	adj[a] = ec;
}

void build(int L, int R)
{
	int i = idx(L, R), mid = (L+R)>>1;
	gmax(nodecnt, i);
	if (L == R) return;
	adde(i, idx(L, mid), 0);
	adde(i, idx(mid+1, R), 0);
	build(L, mid), build(mid+1, R);
}

void quary(int t, int l, int r, int L, int R)
{
	if (L>r || R<l) return;
	if (L>=l && R<=r)
	{
		adde(t, idx(L, R), 1);
		return;
	}
	int mid = (L+R)>>1;
	quary(t, l, r, L, mid);
	quary(t, l, r, mid+1, R);
}

int que[MAXV];
void toposort()
{
	int l = 1, r = 0, u, v;
	rep(i, 1, vn) if (!ind[i]) que[++r] = i;
	if (a[idx(1, N)]==-1) a[idx(1, N)] = mxi;
	while (l <= r)
	{
		u = que[l++];
		vis[u] = 1;
		for (int i = adj[u]; i; i=e[i].nxt)
		{
			v = e[i].to;
			if (~a[v]) gmin(a[v], a[u] - e[i].d);
			else a[v] = a[u] - e[i].d;
			if (ax[v] && a[v] < ax[v]) noso();
			if (!--ind[v]) que[++r] = v;
		}
	}
}

int main()
{
	get(N), get(S), get(M);
	build(1, N), vn = nodecnt;
	memset(a, -1, sizeof a);
	int p, d, l, r, K;
	rep(i, 1, S)
	{
		get(p), get(d);
		if (ax[idx(p, p)] && ax[idx(p, p)]!=d) noso();
		ax[idx(p, p)] = a[idx(p, p)] = d;
	}
	rep(i, 1, M)
	{
		get(l), get(r), get(K);
		++vn, que[1] = l - 1;
		rep(j, 2, K+1)
		{
			get(que[j]);
			adde(idx(que[j], que[j]), vn, 0);
		}
		que[K+2] = r + 1;
		rep(j, 2, K+2)
		{
			l = que[j-1]+1, r = que[j]-1;
			if (l <= r) quary(vn, l, r, 1, N);
		}
	}
	toposort();
	rep(i, 1, N) if (!vis[idx(i, i)] || a[idx(i, i)]<1) noso();
	puts("TAK");
	rep(i, 1, N) printf("%d%c", a[idx(i, i)], i!=N?' ':'\n');
	return 0;
}
    原文作者:拓扑排序
    原文地址: https://blog.csdn.net/u011542204/article/details/50769451
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞