BZOJ4383: [POI2015]Pustynia

题目大意:给出一些限制条件,每个限制条件都是一个区间内k个数比另外一些都大,问合不合法

题目落下个条件   ∑k≤30W


首先把这些限制条件拆开,每个不连续的区间拆成O(K)个连续的区间,然后就变成了KlogK个限制条件,每个条件是一个数比一段区间里的数都大

这个可以连了边之后用记忆化搜索拓扑序Dp就好了

但是连边是O(N^2)级别的,怎么办呢?

就可以用线段树优化建图啦!

首先对于所有的数建一颗线段树,然后一个点对一个区间连边就相当于对线段树上这个区间连边,这是O(logN)级别的

所以总边数就是O(KlogN)级别的,就可以过了


注意这题有坑点,“每个数都在1到10^9范围内”,这句话是有用的!!!!!!!

#include<iostream>
#include<cstdio>
#define N 600010
#define M 2000010
using namespace std;
int a[N],l[N],r[N],ch[N][2],cn;
int sit[N];
int to[M],w[M],nxt[M],pre[N],cnt;
int du[N];
bool done[N];
void ae(int ff,int tt,int ww)
{
	cnt++;
	du[tt]++;
	to[cnt]=tt;
	nxt[cnt]=pre[ff];
	pre[ff]=cnt;
	w[cnt]=ww;
}
int build(int ll,int rr)
{
	cn++;
	int x=cn;
	l[cn]=ll;r[cn]=rr;
	if(ll==rr)
	{
		sit[ll]=cn;
		return x;
	}
	int mid=(ll+rr)>>1;
	ch[x][0]=build(ll,mid);
	ch[x][1]=build(mid+1,rr);
	ae(x,ch[x][0],0);
	ae(x,ch[x][1],0);
	return x;
}
void add(int x,int now,int L,int R)
{
	if(l[now]==L&&r[now]==R)
	{
		ae(x,now,0);
		return;
	}
	int mid=(l[now]+r[now])>>1;
	if(R<=mid) add(x,ch[now][0],L,R);
	else if(L>mid) add(x,ch[now][1],L,R);
	else add(x,ch[now][0],L,mid),add(x,ch[now][1],mid+1,R);
}
void dfs(int x)
{
	done[x]=true;
	int i,j;
	for(i=pre[x];i;i=nxt[i])
	{
		j=to[i];
		a[j]=min(a[j],a[x]-w[i]);
		du[j]--;
		if(!du[j]) dfs(j);
	}
}
int c[N],d[N];
int main()
{
	int n,s,m;
	scanf("%d%d%d",&n,&s,&m);
	int i,j,k,x,y;
	build(1,n);
	int tmp=cn;
	for(i=1;i<=cn;i++) a[i]=1e9;
	for(i=1;i<=s;i++)
	{
		scanf("%d%d",&c[i],&d[i]);
		a[sit[c[i]]]=d[i];
	}
	int L,R;
	while(m--)
	{
		scanf("%d%d%d",&L,&R,&k);
		y=L;cn++;
		for(i=1;i<=k;i++)
		{
			scanf("%d",&x);
			ae(sit[x],cn,1);
			if(x>y) add(cn,1,y,x-1);
			y=x+1;
		}
		if(R>=y) add(cn,1,y,R);
	}
	for(i=tmp+1;i<=cn;i++)
	a[i]=1e9;
	dfs(1);
	for(i=1;i<=n;i++)
	if(!done[sit[i]]||a[sit[i]]<1)
	{
		puts("NIE");
		return 0;
	}
	for(i=1;i<=s;i++)
	if(a[sit[c[i]]]!=d[i])
	{
		puts("NIE");
		return 0;
	}
	puts("TAK");
	for(i=1;i<=n;i++)
	printf("%d ",a[sit[i]]);
}

你可能感兴趣的:(BZOJ4383: [POI2015]Pustynia)