zoukankan      html  css  js  c++  java
  • 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;
    } 
  • 相关阅读:
    计算机网络(1)----概述
    博客园自定义样式
    linux进程
    接口回调解析
    优先级队列
    双栈实现队列
    递归解决反转链表的一部分
    Multisim 之逻辑转换仪
    Multisim 如何添加文本 如何编辑文本字体
    Multisim 中的一些快捷键
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/11729907.html
Copyright © 2011-2022 走看看