P3588 [POI2015]PUS(拓扑排序+线段树)

时间:2019-10-24
本文章向大家介绍P3588 [POI2015]PUS(拓扑排序+线段树),主要包括P3588 [POI2015]PUS(拓扑排序+线段树)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

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;
} 

原文地址:https://www.cnblogs.com/kafuuchino/p/11729907.html