【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;
}
相关文章
相关标签/搜索