【agc028E】High Elements(動態規劃,線段樹,貪心)

【agc028E】High Elements(動態規劃,線段樹,貪心)

題面

AtCoder
你有一個\([1,N]\)的排列\(P\)
一個長度爲\(N\)的字符串\(S\)是好的,當且僅當:ios

  • 兩個序列\(X,Y\)這樣構造:
    一開始,令\(X,Y\)都是空的。而後對於每個\(i=1,2,...,N\),依次考慮每個\(P_i\),若是\(S_i=0\),那麼加入到\(X\)末尾,不然加入到\(Y\)末尾。
  • \(X,Y\)的前綴最大值的個數相等。

如今你要求出一個字典序最小的\(S\)ui

題解

顯然考慮貪心,只要可以放\(0\)咱們就會盡量的放\(0\),。
首先來證實一個結論,若是一個\(S\)合法,那麼一定可以使得\(X,Y\)兩個數列中,有一個數列中的前綴最大值全是排列\(P\)中的前綴最大值。
這樣子考慮,若是兩個數列中都存在不是本來前綴最大值的位置,那麼交換這兩個位置,由於它們本來不是最大值,如今變成了前綴最大值,意味着前綴中比它大而且在它前面的數必定在另一個集合中,那麼交換以後兩個串的前綴最大值的個數都會減小\(1\),那麼通過若干次交換以後必定可以使得一個集合中的前綴最大值全是\(P\)中的前綴最大值。
那麼咱們不妨令\(X\)由原串的前綴最大值構成。
咱們假設前\(i-1\)位已經構造完畢,如今考慮第\(i\)位能夠放哪裏。咱們先強制放到一個位置,而後來判斷是否合法。
不妨令\(X\)中的前綴最大值個數是\(cnt_X\)\(Y\)中的前綴最大值個數爲\(cnt_Y\)
\([i,n]\)\(P\)中本來的前綴最大值的個數是\(Q\),而\(Y\)在接下來的數列中將會有\(k\)個最大值是\(P\)的前綴最大值,則\(X\)中會有\(Q-k\)個。再設\(Y\)中非\(P\)的前綴最大值的前綴最大值個數爲\(m\),則要知足條件:
\[cnt_X+(Q-k)=cnt_Y+k+m\]
化簡後獲得:
\[2k+m=cnt_X+Q-cnt_Y\]
其中右邊是已知的常量了。
不難發現這個東西就是在搭配\(Y\)的數列,那麼把\(P\)中的前綴最大值當作\(2\),其餘的值當作\(1\),因而問題變成了咱們要在後面找出一個序列,使得其前綴最大值的前綴和是右邊的常數。由於若是可以令左邊的值爲\(x\)的話,那麼一定可以獲得\(x-2\)(刪掉一段後綴必定能夠作到)。因此咱們只要維護\(x\)爲奇數和偶數時可以取到的最大值。
同理也能夠反過來,變成搭配\(X\)的數列,同樣的進行一次查詢。
\(f[i][0/1]\)表示以\(i\)開頭的,權值爲偶數/奇數的最大值,轉移的時候能夠從一段區間轉移過來,每次就是區間詢問,單點修改,用線段樹維護便可。spa

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 200200
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,a[MAX],val[MAX],cnt[MAX];
char s[MAX];
struct SegmentTree
{
#define lson (now<<1)
#define rson (now<<1|1)
    int t[MAX<<2];
    void Build(int now,int l,int r)
    {
        if(l==r){t[now]=1-1e9;return;}
        int mid=(l+r)>>1;
        Build(lson,l,mid);Build(rson,mid+1,r);
        t[now]=max(t[lson],t[rson]);
    }
    void Modify(int now,int l,int r,int p,int w)
    {
        if(l==r){t[now]=w;return;}
        int mid=(l+r)>>1;
        if(p<=mid)Modify(lson,l,mid,p,w);
        else Modify(rson,mid+1,r,p,w);
        t[now]=max(t[lson],t[rson]);
    }
    int Query(int now,int l,int r,int L,int R)
    {
        if(L<=l&&r<=R)return t[now];
        int mid=(l+r)>>1,ret=-1e9;
        if(L<=mid)ret=max(ret,Query(lson,l,mid,L,R));
        if(R>mid)ret=max(ret,Query(rson,mid+1,r,L,R));
        return ret;
    }
}Odd,Even;
bool check(int mx,int Q)
{
    if(Q<0)return false;
    if(Q&1)return Odd.Query(1,1,n,mx,n)>=Q;
    else return Even.Query(1,1,n,mx,n)>=Q;
}
int main()
{
    n=read();
    for(int i=1,mx=0;i<=n;++i)
    {
        a[i]=read();
        if(a[i]>mx)val[i]=2,mx=a[i];
        else val[i]=1;
    }
    Odd.Build(1,1,n);
    for(int i=n;i;--i)
    {
        int mx1=Odd.Query(1,1,n,a[i],n),mx0=Even.Query(1,1,n,a[i],n);
        if(val[i]&1)Odd.Modify(1,1,n,a[i],mx0+val[i]),Even.Modify(1,1,n,a[i],mx1+val[i]);
        else Odd.Modify(1,1,n,a[i],mx1+val[i]),Even.Modify(1,1,n,a[i],mx0+val[i]);
    }
    for(int i=n;i;--i)cnt[i]=cnt[i+1]+val[i]-1;
    int cntX=0,cntY=0,mxX=0,mxY=0;
    for(int i=1;i<=n;++i)
    {
        Odd.Modify(1,1,n,a[i],1-1e9);Even.Modify(1,1,n,a[i],0);
        if(check(mxY,cntX+cnt[i+1]-cntY+(a[i]>mxX)))s[i]='0',cntX+=a[i]>mxX,mxX=max(mxX,a[i]);
        else if(check(max(mxX,a[i]),cntY+cnt[i+1]-cntX-(a[i]>mxX)))s[i]='0',cntX+=a[i]>mxX,mxX=max(mxX,a[i]);
        else s[i]='1',cntY+=a[i]>mxY,mxY=max(mxY,a[i]);
    }
    if(cntX!=cntY){puts("-1");}
    else printf("%s",s+1);
    return 0;
}
相關文章
相關標籤/搜索