• 洛谷P3588


    Portal

    Description

    给定一个长度为(n(nleq10^5))的正整数序列({a_n}),每个数都在([1,10^9])范围内,告诉你其中(s)个数,并给出(m(mleq2 imes10^5))条信息。每条信息包含三个数(L,R,k(Σkleq 3 imes10^5))以及(k)个正整数({x_k}),表示(a_L..a_R)中,任意一个(x)均比剩下的(R-L+1-k)个数大(严格大于,即没有等号)。请任意构造出一组满足条件的方案,或者判断无解。

    Solution

    拓扑排序+线段树优化建图。
    定义点权为(val);存在一条边权为(w)的边((u,v))表示(val[v]geq val[u]+w)
    首先考虑朴素的做法。建立(n)个点,(val[i])表示(a_i)的值。对于每一条信息,新建一个点(p)(val[p])表示(min{x_k});剩下的数分别向(p)连一条边权为(1)的边((min{x_k})大于剩下的数),(p)(x_1..x_k)分别连一条边权为(0)的边((x_i)大于等于(min{x_k}))。初始时入度为(0)的点若没有值则令其(val=1),然后进行拓扑排序,如果成环或与初值冲突则无解。这样共有(O(nm))条边。
    考虑到边权为(1)的边的起点相当于(k+1)个区间,我们可以用线段树来优化建图。举例:(n=8,a_3=7,a_5=4,a_7=2)([1,4])中最大的是({2,3})([4,8])中是({6})([1,8])中是({2})

    其中虚线边的权值为(0),实线边的权值为(1)。对于蓝线以上的点(线段树上的点),其(val)表示区间中的最大值;对于蓝线以下的点(条件所代表的点),其(val)表示(min{x_k})。意义还是很明确的:例如边(([3,4],{2}))的权值为(1),表示(min{a_2}>max{3,4})。同样机型拓扑排序并判断无解即可。至于边数...线段树上有(2n)条边,(m)条信息总共划分了(m+Σk)个区间,每个区间对应(O(logn))条边,({x_k})共对应(Σk)条边;共计约(2n+Σk+(m+Σk)logn)条边。当然实际上要小很多,因为一条信息中所有区间的和是([1,n]),每个区间对应的边数远不足(logn)算这么多干嘛直接开vector能过就行啊

    Code

    //[POI2015]Pustynia
    #include <cstdio>
    #include <queue>
    #include <vector>
    using namespace std;
    inline char gc()
    {
        static char now[1<<16],*s,*t;
        if(s==t) {t=(s=now)+fread(now,1,1<<16,stdin); if(s==t) return EOF;}
        return *s++;
    }
    inline int read()
    {
        int x=0; char ch=gc();
        while(ch<'0'||'9'<ch) ch=gc();
        while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
        return x;
    }
    inline int max(int x,int y) {return x>y?x:y;}
    const int N=1e5+10;
    int n,n1,m;
    int cnt,rt,chL[N<<1],chR[N<<1];
    const int N1=4e5+10;
    int edCnt;
    int val[N1],a[N1],in[N1],id[N];
    vector< pair<int,bool> > son[N1];
    inline void edAdd(int u,int v,bool w) {edCnt++; in[v]++; son[u].push_back(make_pair(v,w));}
    void bldTr(int &p,int L0,int R0)
    {
        if(!p) p=++cnt;
        if(L0==R0) {id[L0]=p; return;}
        int mid=L0+R0>>1;
        bldTr(chL[p],L0,mid),bldTr(chR[p],mid+1,R0);
        edAdd(chL[p],p,0),edAdd(chR[p],p,0);
    }
    int optL,optR;
    void trEdAdd(int p,int L0,int R0)
    {
        if(optL<=L0&&R0<=optR) {edAdd(p,cnt,1); return;}
        int mid=L0+R0>>1;
        if(optL<=mid) trEdAdd(chL[p],L0,mid);
        if(mid<optR) trEdAdd(chR[p],mid+1,R0);
    }
    queue<int> Q;
    int main()
    {
        n=read(),n1=read(),m=read();
        bldTr(rt,1,n);
        for(int i=1;i<=n1;i++) {int u=id[read()]; val[u]=a[u]=read();}
        for(int i=1;i<=m;i++)
        {
            int L=read(),R=read(),k0=read();
            cnt++; int pre=L;
            while(k0--)
            {
                int x=read();
                optL=pre,optR=x-1; if(optL<=optR) trEdAdd(rt,1,n);
                pre=x+1; edAdd(cnt,id[x],0);
            }
            optL=pre,optR=R; if(optL<=optR) trEdAdd(rt,1,n);
        }
        for(int u=1;u<=cnt;u++) if(!in[u]) val[u]=max(1,a[u]),Q.push(u);
        bool noAns=false;
        while(!noAns&&!Q.empty())
        {
            int u=Q.front(); Q.pop();
            if(a[u]&&val[u]>a[u]) {noAns=true; break;}
            for(int i=0;i<son[u].size();i++)
            {
                int v=son[u][i].first,w=son[u][i].second;
                val[v]=max(val[v],val[u]+w);
                if(--in[v]==0) Q.push(v);
            }
        }
        for(int u=1;u<=cnt&&!noAns;u++) if(in[u]||val[u]>1e9) noAns=true;
        if(noAns) {puts("NIE"); return 0;}
        puts("TAK");
        for(int i=1;i<=n;i++) printf("%d ",val[id[i]]);
        puts("");
        return 0;
    }
    

    P.S.

    每个数都在([1,10^9])范围内! 意思是说如果你推出来有的数大于(10^9)就是无解,坑我半天...

  • 相关阅读:
    build/envsetup.sh 脚本分析 lunch函数
    DEDE自动审核会员发表的最新文章的修改方法
    php学习笔记(三)――操作符与控制结构
    简单的会话类
    学习PHP几个数学计算的内部函数
    关于gotozf学习笔记(11/08/06 12:07)
    再谈中文分词php类
    原站完整打包PHP+MYSQL,可做仿53客服在线客服系统,多用户多国语言企业版
    白话经典算法系列之二 直接插入排序的三种实现
    php 代码运行时间查看类
  • 原文地址:https://www.cnblogs.com/VisJiao/p/LgP3588.html
Copyright © 2020-2023  润新知