codeforces-#686-F

codeforces-#686-F

十一月 27, 2020

F. Array Partition

  • 题意

    给你一个长为 n (3≤n≤2e5) 的数组 an,问是否能把它分成三段,

    第一段的最大值等于第二段的最小值等于第三段的最大值。若能,输出分割方案。

  • 题解

    参考博客

    很显然题目 需要处理一个前缀最大值 后缀最大值 以及用线段树

    之类的数据结构维护中间部分的最小值 枚举分割方式判定是否符合要求。

    解决问题的关键在于如何枚举分割区域。

    根据题目要求 符合条件的分割方式一定能使得中间区域的Min >= x 两侧区域的Max<=x 且都能取到 = x

    所以枚举的x必然存在于三个区域中。

    于是可以枚举每个 $a_i$ 作为上述的变量x 向左寻找最后一个以$a_i$为Max的 边界点L,

    同时向右寻找最后一个以$a_i$ 为Max的边界点R (下图中黑圈表示等于$a_i$)

    当然$a_i$是否能成为中间区域的Min 需要进行验证

目前已经确保中间部分存在$a_i$ (但仍不能确定$a_i$是Min)且左右区间确保最大值为$a_i$

为什么取以当前位置$a_i$ 左右两侧最近的能同时使得$a_i$成为前后缀最大值的 位置作为L,R

因为[1,L]的前缀确保了所有数都小于等于$a_i$ [R,n]的后缀确保了所有数都小于等于$a_i$ 那么

让[L+1,R-1]作为中间区域 能尽量避免两边小于$a_i$的数加入 成为其最小值

于是我们只要判定 Max[1,L] == Min[L+1,R-1] == Max [R,n] 是否成立就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

#define endl '\n'
#define Turnoff std::ios::sync_with_stdio(false)
//#define P pair<ll,ll>
const int Max=2e5+5;
const int Mod=998244353;
const int inf=0x3f3f3f3f;

//#define __DEBUG__
#ifdef __DEBUG__
#define DeBug(format, ...) \
{ \
fprintf(stdout, "[%s:%d]" format "", \
__FUNCTION__ , __LINE__, ##__VA_ARGS__); \
}
#else
#define DeBug(format,...)
#endif

class SegTree{
public:
#define Mid ((L+R)>>1)
#define Leftson now<<1
#define Rightson now<<1|1
struct segtree{int L,R,minn,lazy;};
segtree tree[Max<<2];
void push_up(int now){
tree[now].minn=min(tree[Leftson].minn,tree[Rightson].minn);
return;
}
void build(int L,int R,int now=1){
tree[now]={L,R,0,0};
//cout<<L<<" "<<R<<endl;
if(L==R)return;
//int mid=(L+R)>>1;
build(L,Mid,Leftson);
build(Mid+1,R,Rightson);
push_up(now);
return;
}
void updata(int qL,int qR,int val,int now=1){
if(qL<=tree[now].L&&qR>=tree[now].R){
tree[now].minn+=val;
return;
}
if(qL<=tree[Leftson].R)updata(qL,qR,val,Leftson);
if(qR>=tree[Rightson].L)updata(qL,qR,val,Rightson);
push_up(now);return;
}
int query(int qL,int qR,int now=1){
if(qL>qR)return inf;
if(tree[now].R<=qR&&tree[now].L>=qL)return tree[now].minn;
int minn=inf;
if(tree[Leftson].R>=qL)minn=min(minn,query(qL,qR,Leftson));
if(tree[Rightson].L<=qR)minn=min(minn,query(qL,qR,Rightson));
push_up(now);
return minn;
}
}Min;

int arr[Max];
map<int,int>per,suf;
int pos[Max];

int main(void){
//Turnoff;
int t;cin>>t;
while(t--){
int n;cin>>n;
Min.build(1,n);
suf.clear(),per.clear();
for(int i=1;i<=n;i++){
cin>>arr[i];
pos[i]=n+1;
suf[arr[i]]=n+1;
Min.updata(i,i,arr[i]);
}
int maxn=0;
for(int i=n;i>=1;i--){
pos[i]=suf[arr[i]];
maxn=max(maxn,arr[i]);
suf[maxn]=i;
///cout<<i<<" "<<pos[i]<<endl;
}
maxn=0;
int x=0,y=0,z=0;
for(int i=1;i<=n;i++){
maxn=max(maxn,arr[i]);
int L=per[arr[i]],R=pos[i];
per[maxn]=i;
if(R>n||L<1)continue;
if(Min.query(L+1,R-1)==arr[i]){
x=L,y=R-L-1,z=n-R+1;
}
}
if(x){
cout<<"YES"<<endl;
cout<<x<<" "<<y<<" "<<z<<endl;
}
else cout<<"NO"<<endl;
}
}