AGC028E
作者:互联网
题意
给出一个长度为 \(n\) 的排列 \(P\)。
一个长度为 \(n\) 的 01 串 \(S\) 被成为好的,当且仅当:
- 按照以下方式构造序列 \(X,Y\)
-
- 首先我们让 \(X,Y\) 为空的序列
- 对于每一个 \(i=1,2,\ldots,N\),按照顺序,如果 \(S_i=0\),在 \(X\) 后面添加 \(P_i\);如果 \(S_i=1\),在 \(Y\) 后面添加 \(P_i\)
- \(X,Y\) 的前缀最大值的个数相同
判断是否存在一个好的字符串,并且输出字典序最小的那一个。\(n \leq 2 \times 10^5\)
题解
最小化字典序,我们就会考虑如何去判断某个确定的前缀是否合法。
我们将 \(X,Y\) 内的前缀最大值分成两类:原值和新值。原值的意思就是 \(P\) 中的前缀最大值,新值就指的不是 \(P\) 中的前缀最大值。
我们考虑确定好这个前缀后,设 \(X,Y\) 的前缀最大值数量为 \(x,y\),一个结论是:如果这个前缀合法,那么一定有至少一个序列,满足还没填的那一块只有原值。
证明:假设都有新值的话,考虑是 \(a,b\),\(a\) 在 \(X\) 内是新值的原因是,把它挡住的原值被放到了 \(Y\) 里;\(b\) 同理。所以交换 \(a,b\) 后这两个新值都消失了,并且还是保证前缀最大值相同的。所以肯定能把一个序列的新值个数削成 \(0\)。
于是我们就可以找一下等量关系:设 \(k\) 表示还没填的 \(P\) 中原值的数量。我们设是 \(A\) 的后面只有原值,那么设 \(B\) 有 \(a\) 个原值,\(b\) 个新值,\(A\) 就有 \(k-a\) 个原值,可以列出等式:
\[\begin{align*} a+b+y &= k-a+x\\ 2a+b &= k+x-y \end{align*} \]我们考虑找一下 \(2a+b\) 的意义:就是原值的贡献是 \(2\),新值的贡献是 \(1\)。所以问题就在于,能否从 \(B\) 后面选出一个上升的序列,满足拼起来之后还是一个前缀最大值,并且分数是 \(2a+b\)。
但是这样如果直接 dp 的话是二维的状态不好 dp。但是我们观察到:如果能选出一个 \(x \geq 2\),一定能选出一个值为 \(x-2\) 的 dp。所以我们只关心当前情况下能选出的最大奇数/偶数分数。那么就可以设 \(f_{i,0/1}\) 表示考虑后缀 \(i\) ,强制选择 \(a_i\),偶数/奇数的最大得分,转移就是:
\[f_{i,x} = \max_{j > i,a_j > a_i} f_{j,x'} \]从后向前 dp,只需要考虑第二个限制,线段树优化复杂度 \(O(n \log n)\)。
不过注意一下我们这里查询的是 \(i \geq x,a_i \geq a_x\) 的最大值,所以最后还是要倒着来一遍线段树的。
\(Y\) 是只有原值的情况类似推式子集合。等式是 \(2a+b = k+y-x\)。
启发:
- 在做物品权值的时候,如果有性质如果 \(x\) 可以,那么 \(x-a\) 也可以,就可以考虑求出 \(\bmod a = i\) 的最大/最小值来优化。有的时候需要跑最短路(其实这个就是同余最短路,也告诉我们某些背包问题也可以用这个东西优化)
- 这个题一开始 \(A,B\) 原值新值都有,我们注意到原值不管在哪里都是前缀最大值,所以我们要考虑能否调整让问题变得简单。
代码
#include <bits/stdc++.h>
#define fi first
#define se second
#define DB double
#define U unsigned
#define P std::pair
#define LL long long
#define LD long double
#define pb emplace_back
#define MP std::make_pair
#define SZ(x) ((int)x.size())
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int MAXN = 2e5 + 5;
struct DS{
int mx[MAXN<<2];
DS(){CLR(mx,~0x3f);}
#define lc ((x)<<1)
#define rc ((x)<<1|1)
inline void update(int x,int l,int r,int p,int d){
if(l == r) return (void)(mx[x] = d);
int mid = (l + r) >> 1;
if(p <= mid) update(lc,l,mid,p,d);
else update(rc,mid+1,r,p,d);
mx[x] = std::max(mx[lc],mx[rc]);
}
inline int query(int x,int l,int r,int L,int R){
if(L == 0) L = 1;
if(l == L && r == R) return mx[x];
int mid = (l + r) >> 1;
if(R <= mid) return query(lc,l,mid,L,R);
if(L > mid) return query(rc,mid+1,r,L,R);
return std::max(query(lc,l,mid,L,mid),query(rc,mid+1,r,mid+1,R));
}
}tr[2];
int n,a[MAXN];
bool mx[MAXN];
int res[MAXN];
int main(){
scanf("%d",&n);
FOR(i,1,n) scanf("%d",a+i);
int t = 0;
FOR(i,1,n){
t = std::max(t,a[i]);
mx[i] = t == a[i];
}
tr[0].update(1,1,n+1,n+1,0);
ROF(i,n,1){
int f[2];CLR(f,~0x3f);
FOR(j,0,1) f[j] = tr[j^mx[i]^1].query(1,1,n+1,a[i],n+1)+1+mx[i];
FOR(j,0,1) tr[j].update(1,1,n+1,a[i],f[j]);
}
int x = 0,y = 0,k = 0,lx = 0,ly = 0;
FOR(i,1,n) k += mx[i];
FOR(i,1,n){
k -= mx[i];
FOR(j,0,1) tr[j].update(1,1,n+1,a[i],~0x3f3f3f3f);
// 0
x += lx < a[i];
if((x+k-y >= 0 && tr[(x+k-y)%2].query(1,1,n+1,ly,n+1) >= x+k-y) || (k+y-x >= 0 && tr[(k+y-x)%2].query(1,1,n+1,std::max(lx,a[i]),n+1) >= k+y-x)){
res[i] = 0;lx = std::max(lx,a[i]);
continue;
}
x -= lx < a[i];y += ly < a[i];
res[i] = 1;ly = std::max(ly,a[i]);
}
if(x != y) return puts("-1"),0;
FOR(i,1,n) putchar('0'+res[i]);
puts("");
return 0;
}
标签:std,原值,前缀,AGC028E,int,新值,define 来源: https://www.cnblogs.com/rainair/p/14456523.html