P3588 [POI2015]PUS(拓扑排序+线段树)
Posted kafuuchino
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3588 [POI2015]PUS(拓扑排序+线段树)相关的知识,希望对你有一定的参考价值。
对于每个$(l,r,k)$,将$k$个位置向剩下$r-l-k+1$个位置连边,边权为$1$,这样就保证$k$个位置比剩下的大
先给所有位置填$1e9$保证最优
然后拓扑排序填数
填的数不在$[1,1e9]$内或者出现环,即为不合法
但是这样边数过多会超时
于是考虑线段树优化建图
把$n$个点建成线段树,每个节点向左右儿子连边,边权为0。
这样每次连一个区间$[l,r]$就只需要$log(r-l+1)$次
注意不合法情况要枚举完整
#include<iostream> #include<cstdio> using namespace std; #define N 100005 #define M 6000005 int n,s,m,u,pos[N],h[N*6],L,R,no; int p[N*6],w[N*6],id[N]; bool vis[N*6]; int Cnt,hd[N*6],nxt[M],ed[N*6],poi[M],val[M],in[N*6]; void adde(int x,int y,int v){ nxt[ed[x]]=++Cnt; hd[x]=hd[x]?hd[x]:Cnt; ed[x]=Cnt; poi[Cnt]=y; val[Cnt]=v; ++in[y]; } #define mid (l+r)/2 int build(int o,int l,int r){ w[p[o]=++u]=1e9; if(l==r) return id[l]=u; adde(p[o],build(o<<1,l,mid),0); adde(p[o],build(o<<1|1,mid+1,r),0); return p[o]; } void Add(int o,int l,int r,int x1,int x2,int k){ if(x1<=l&&r<=x2){adde(k,p[o],0); return ;} if(x1<=mid) Add(o<<1,l,mid,x1,x2,k); if(x2>mid) Add(o<<1|1,mid+1,r,x1,x2,k); } void work(){ int tt=0; for(int i=1;i<=u;++i) if(!in[i]) h[++R]=i; while(L!=R){ if(L>=N) L=0; int x=h[++L]; ++tt; if(w[x]<1) no=1;//填的数<1 for(int i=hd[x];i;i=nxt[i]){ int to=poi[i]; if(vis[to]&&w[to]>w[x]-val[i]) no=1;//填的数比已给定位置上的数值小 w[to]=min(w[to],w[x]-val[i]); if((--in[to])==0){ if(R>=N) R=0; h[++R]=to; } } } if(tt<u) no=1;//图中有环 } int main(){ scanf("%d%d%d",&n,&s,&m); build(1,1,n); for(int i=1,Id,v;i<=s;++i){ scanf("%d%d",&Id,&v); if(v<1||v>1e9) no=1;//给定数不合法 w[id[Id]]=v; vis[id[Id]]=1; } for(int i=1,l,r,k;i<=m;++i){ scanf("%d%d%d",&l,&r,&k); w[++u]=1e9;//新建一个中转节点 for(int j=1;j<=k;++j) scanf("%d",&pos[j]),adde(id[pos[j]],u,1); if(l<pos[1]) Add(1,1,n,l,pos[1]-1,u); if(r>pos[k]) Add(1,1,n,pos[k]+1,r,u); for(int j=1;j<k;++j) if(pos[j]+1<pos[j+1]) Add(1,1,n,pos[j]+1,pos[j+1]-1,u); }work(); if(no) puts("NIE"); else{ puts("TAK"); for(int i=1;i<=n;++i) printf("%d ",w[id[i]]); }return 0; }
以上是关于P3588 [POI2015]PUS(拓扑排序+线段树)的主要内容,如果未能解决你的问题,请参考以下文章