1. 程式人生 > >洛谷P3588 - [POI2015]Pustynia

洛谷P3588 - [POI2015]Pustynia

using png 拓撲 -i org name 則無 const tdi

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