洛谷P3588 - [POI2015]Pustynia

Posted

V

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷P3588 - [POI2015]Pustynia相关的知识,希望对你有一定的参考价值。

Portal

Description

给定一个长度为\\(n(n\\leq10^5)\\)的正整数序列\\(\\{a_n\\}\\),每个数都在\\([1,10^9]\\)范围内,告诉你其中\\(s\\)个数,并给出\\(m(m\\leq2\\times10^5)\\)条信息。每条信息包含三个数\\(L,R,k(Σk\\leq 3\\times10^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\\)就是无解,坑我半天...

以上是关于洛谷P3588 - [POI2015]Pustynia的主要内容,如果未能解决你的问题,请参考以下文章

洛谷P3585 [POI2015]PIE

BZOJ 4385 洛谷3594 POI2015 WIL-Wilcze do?y

[洛谷P3501] [POI2010]ANT-Antisymmetry

洛谷 P3467 [POI2008]PLA-Postering

洛谷 P3496 [POI2010]GIL-Guilds

洛谷P3469 [POI2008]BLO-Blockade