【bzoj4383】[POI2015]Pustynia【拓扑排序】【线段树优化建图】

其实就是一些大小关系。我们设一条边 u → v u\rightarrow v uv代表 u > v u>v u>v或者 u ≤ v u\le v uv,这要看具体情况,或者说分两类。对于每个限制,我们可以开一个虚点,每个大的向虚点连一条 ≤ \le 的边,然后发现虚点会向若干段连续区间连边,直接线段树优化连边就好了。最后填数时,倒过来贪心,深度越深,就贪心取越小。其实这就是一个DAG上的dp。
在xsy过了,在lydsy上因常数过大TLE了。。。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#pragma GCC optimize(3)
using namespace std;
const int N=100005,M=1000005;
int n,s,m,p,d,l,r,k,cnt,tot,rt,x[N],f[M],ch[M][2],in[M],pos[M];
bool vis[M],ck[M];
queue<int> q;
vector<int> e1[M],e2[M];
void adde(int u,int v){
	e1[u].push_back(v);
	in[v]++;
	e2[v].push_back(u);
}
void build(int &o,int l,int r){
	o=++tot;
	if(l==r){
		adde(o,l);
		return;
	}
	int mid=(l+r)/2;
	build(ch[o][0],l,mid);
	build(ch[o][1],mid+1,r);
	adde(o,ch[o][0]);
	adde(o,ch[o][1]);
}
void update(int o,int l,int r,int L,int R,int v){
	if(L<=l&&R>=r){
		adde(v,o);
		return;
	}
	int mid=(l+r)/2;
	if(L<=mid){
		update(ch[o][0],l,mid,L,R,v);
	}
	if(R>mid){
		update(ch[o][1],mid+1,r,L,R,v);
	}
}
int main(){
	scanf("%d%d%d",&n,&s,&m);
	for(int i=1;i<=s;i++){
		scanf("%d%d",&p,&d);
		f[p]=d;
		ck[p]=true;
	}
	tot=n;
	build(rt,1,n);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&l,&r,&k);
		tot++;
		for(int j=1,last=l-1;j<=k;last=x[j],j++){
			scanf("%d",&x[j]);
			adde(x[j],tot);
			if(last+1<x[j]){
				update(rt,1,n,last+1,x[j]-1,tot);
			}
		}
		if(x[k]<r){
			update(rt,1,n,x[k]+1,r,tot);
		}
	}
	for(int i=1;i<=tot;i++){
		if(!in[i]){
			q.push(i);
		}
	}
	while(!q.empty()){
		int u=q.front(),v;
		q.pop();
		pos[++pos[0]]=u;
		vis[u]=true;
		for(int i=0;i<(int)e1[u].size();i++){
			v=e1[u][i];
			in[v]--;
			if(!in[v]){
				q.push(v);
			}
		}
	}
	for(int i=pos[0];i>=1;i--){
		if(!f[pos[i]]){
			f[pos[i]]=1;
		}
		for(int j=0;j<(int)e2[pos[i]].size();j++){
			int v=e2[pos[i]][j];
			if(pos[i]<=n){
				if(ck[v]){
					if(f[pos[i]]+1>f[v]){
						puts("NIE");
						return 0;
					}
				}else{
					f[v]=max(f[v],f[pos[i]]+1);
				}
			}else{
				if(ck[v]){
					if(f[pos[i]]>f[v]){
						puts("NIE");
						return 0;
					}
				}else{
					f[v]=max(f[v],f[pos[i]]);
				}
			}
		}
	}
	for(int i=1;i<=n;i++){
		if(!vis[i]||f[i]>1000000000||f[i]<1){
			puts("NIE");
			return 0;
		}
	}
	puts("TAK");
	for(int i=1;i<=n;i++){
		printf("%d ",f[i]);
	}
	puts("");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值