zoukankan      html  css  js  c++  java
  • BZOJ_4383_[POI2015]Pustynia_线段树优化建图+拓扑排序

    BZOJ_4383_[POI2015]Pustynia_线段树优化建图+拓扑排序

    Description

    给定一个长度为n的正整数序列a,每个数都在1到10^9范围内,告诉你其中s个数,并给出m条信息,每条信息包含三个数l,r,k以及接下来k个正整数,表示a[l],a[l+1],...,a[r-1],a[r]里这k个数中的任意一个都比任意一个剩下的r-l+1-k个数大(严格大于,即没有等号)。
    请任意构造出一组满足条件的方案,或者判断无解。

    Input

    第一行包含三个正整数n,s,m(1<=s<=n<=100000,1<=m<=200000)。
    接下来s行,每行包含两个正整数p[i],d[i](1<=p[i]<=n,1<=d[i]<=10^9),表示已知a[p[i]]=d[i],保证p[i]递增。
    接下来m行,每行一开始为三个正整数l[i],r[i],k[i](1<=l[i]<r[i]<=n,1<=k[i]<=r[i]-l[i]),接下来k[i]个正整数x[1],x[2],...,x[k[i]](l[i]<=x[1]<x[2]<...<x[k[i]]<=r[i]),表示这k[i]个数中的任意一个都比任意一个剩下的r[i]-l[i]+1-k[i]个数大。Σk <= 300,000

    Output

    若无解,则输出NIE。
    否则第一行输出TAK,第二行输出n个正整数,依次输出序列a中每个数。

    Sample Input

    5 2 2
    2 7
    5 3
    1 4 2 2 3
    4 5 1 4

    Sample Output

    TAK
    6 7 1000000000 6 3

    有一种朴素的方法就是小的连到大的,边权为1,然后拓扑排序,给每个点分配一个合法且尽可能小的权值。
    最后看是否有点没更新到,以及最大的权值是否超过上限。
    现在的问题相当于区间连边,用一棵线段树维护一下即可。
     
    代码:
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define N 200050
    int head[N<<3],to[N*12],nxt[N*12],val[N*12],cnt,in[N<<3],dis[N<<3],Q[N<<3],l,r,vis[N<<3];
    int tot,ls[N<<2],rs[N<<2],root,a[N],idx[N],n,m;
    void add(int u,int v,int w) {
    	to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; val[cnt]=w; in[v]++;
    }
    void build(int l,int r,int &p) {
    	p=++tot;
    	if(l==r) {idx[l]=p;return ;}
    	int mid=(l+r)>>1;
    	build(l,mid,ls[p]); build(mid+1,r,rs[p]);
    	add(ls[p],p,0); add(rs[p],p,0);
    }
    void update(int l,int r,int x,int y,int p) {
    	if(x<=l&&y>=r) {
    		add(p,tot,0); return ;
    	}
    	int mid=(l+r)>>1;
    	if(x<=mid) update(l,mid,x,y,ls[p]);
    	if(y>mid) update(mid+1,r,x,y,rs[p]);
    }
    int main() {
    	int s;
    	scanf("%d%d%d",&n,&s,&m);
    	build(1,n,root);
    	int i,x,y,k,j;
    	for(i=1;i<=s;i++) {
    		scanf("%d%d",&x,&y);
    		dis[idx[x]]=y; vis[idx[x]]=1;
    	}
    	for(i=1;i<=m;i++) {
    		scanf("%d%d%d",&x,&y,&k);
    		a[0]=x-1; tot++;
    		for(j=1;j<=k;j++) {
    			scanf("%d",&a[j]);
    			add(tot,idx[a[j]],1);
    			if(a[j-1]+1<=a[j]-1) update(1,n,a[j-1]+1,a[j]-1,root);
    		}
    		if(a[k]!=y) update(1,n,a[k]+1,y,root);
    	}
    	for(i=1;i<=tot;i++) {
    		if(!in[i]) {
    			Q[r++]=i;
    			dis[i]=max(dis[i],1);
    		}
    	}
    	while(l<r) {
    		x=Q[l++];
    		for(i=head[x];i;i=nxt[i]) {
    			if(vis[to[i]]&&dis[x]+val[i]>dis[to[i]]) {
    				puts("NIE"); return 0;
    			}
    			dis[to[i]]=max(dis[to[i]],dis[x]+val[i]);
    			in[to[i]]--;
    			if(in[to[i]]==0) Q[r++]=to[i];
    		}
    	}
    	for(i=1;i<=n;i++) {
    		if(!dis[idx[i]]||dis[idx[i]]>1000000000) {
    			puts("NIE"); return 0;
    		}
    	}
    	puts("TAK");
    	for(i=1;i<n;i++) printf("%d ",dis[idx[i]]);
    	printf("%d",dis[idx[n]]);
    }
    
  • 相关阅读:
    解决Ubuntu 18.04中文输入法的问题,安装搜狗拼音
    POJ 1151 Atlantis 矩形面积求交/线段树扫描线
    [CTF]思维导向图
    Ubuntu Linux 学习篇 配置DHCP服务器
    Ubuntu Linux 学习篇 配置DNS服务器
    Ubuntu Linux DNS服务器 BIND9配置文件命令介绍
    Ubuntu Linux 学习篇 配置DHCP服务器
    Ubuntu Linux 学习篇 配置DNS服务器
    Ubuntu Linux DNS服务器 BIND9配置文件命令介绍
    随记
  • 原文地址:https://www.cnblogs.com/suika/p/9202336.html
Copyright © 2011-2022 走看看