T1
题意
你有一个数组 a1,a2,…,ana_1,a_2,…,a_na1,a2,…,an,每次你可以进行如下操作:
- 在数组中选择一个数,把它从数组中删除,然后加入数组的开头或者末尾。
问最少多少次能将数组变为单调不减的。
解法
相当于问有多少个数必须进行操作,有多少个数可以不动,设可以不动的数字的下标为 b1,b2,...,bmb_1,b_2,...,b_mb1,b2,...,bm,则 bbb 需要满足如下条件:
- 1≤b1<b2<...<bm≤n1 \le b_1 < b_2 < ... < b_m \le n1≤b1<b2<...<bm≤n
- ab1≤ab2≤...≤abma_{b_1} \le a_{b_2} \le ... \le a_{b_m}ab1≤ab2≤...≤abm
- ∀i∈[1,n]\forall i \in [1,n]∀i∈[1,n],∃j∈[1,m]\exists j \in [1,m]∃j∈[1,m] 满足 i=bji = b_ji=bj 或 ai≤ab1a_i \le a_{b_1}ai≤ab1 或 ai≥abma_i \ge a_{b_m}ai≥abm
证明简单说明③:
因为除了 bbb 中的数都进行了操作,所以所有数必然在 bbb 中的数的前或后,若不满足条件,则数组无序,然而想让数组重新有序,需要把 bbb 中的数移到开头或结尾,不满足定义。
现在问题转化为求 mmm 的最大值,容易发现,bbb 中的数可以分为 333 个部分(不一定 333 个部分都有):
- xxx 出现位置下标序列的前缀
- [x+1,y−1][x + 1,y - 1][x+1,y−1] 出现位置的下标序列
- yyy 出现位置下标序列的后缀
②③的长度可以用类似 dp 的方法预处理,然后枚举①,细节非常多,总复杂度 O(n)O(n)O(n)
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 1931931931;
const int N = 3e5 + 31;
const int M = 1e6 + 31;
int n,a[N],b[N],c[M],nxt[N],f[M],g[N],t[M],v[M];
void solve(){
scanf("%d",&n); c[0] = n + 1; t[0] = 0; v[0] = n + 1;
for(int i = 1;i <= n;i++)
scanf("%d",&a[i]), b[i] = a[i], c[a[i]] = n + 1, g[i] = 0, t[a[i]] = 0;
for(int i = n;i >= 1;i--)
v[a[i]] = i;
g[n + 1] = 0;
sort(b + 1,b + n + 1);
int m = unique(b + 1,b + n + 1) - b - 1;
for(int i = 1;i < m;i++)
f[b[i]] = b[i + 1];
f[b[m]] = 0;
int res = 0;
for(int i = n;i >= 1;i--){
nxt[i] = c[a[i]];
if(c[a[i]] == n + 1)
nxt[i] = (a[i] == b[m] ? n + 1 : v[f[a[i]]]);
if(nxt[i] == n + 1 || nxt[i] < i)
g[i] = t[f[a[i]]] + 1;
else
g[i] = g[nxt[i]] + 1;
res = max(res,g[i]);
c[a[i]] = i; t[a[i]]++;
}
for(int i = 1;i < m;i++){
int j = v[b[i]], tmp = 0;
while(j < v[b[i + 1]])
j = nxt[j], tmp++;
res = max(res,tmp + g[v[b[i + 1]]]);
}
for(int i = 1;i <= n;i++)
v[a[i]] = 0;
for(int i = 1;i <= n;i++){
v[a[i]]++; t[a[i]]--;
res = max(res,v[a[i]] + t[f[a[i]]]);
}
printf("%d\n",n - res);
}
int main(){
int t; scanf("%d",&t);
while(t--) solve();
}
T2
题意
给定一个数组 a1,a2,…,ana_1,a_2,…,a_na1,a2,…,an,有 qqq 组询问,每组询问给定 kkk,求:
maxr−l+1=kmex{al,al+1,...ar} max_{r - l + 1 = k} mex \lbrace a_l,a_{l + 1},...a_r \rbracemaxr−l+1=kmex{al,al+1,...ar}
解法
有一个结论是,对于所有的询问,一定可以被拆成 O(n)O(n)O(n) 个四元组 (l0,l1,r0,r1)(l_0,l_1,r_0,r_1)(l0,l1,r0,r1),保证对所有 l0≤l≤l1,r0≤r≤r1l_0 \le l \le l_1,r_0 \le r \le r_1l0≤l≤l1,r0≤r≤r1 这样的询问,[l,r][l,r][l,r] 的 mex 都是相同的。
对于一类长度越长,值单调递增或者递减的区间,如果要求最值,那么越短的区间肯定比较长的区间要优,所以我们要把较长的区间直接删掉,最后就剩下了 O(n)O(n)O(n) 个不交区间。我们可以直接求这个不交区间。这个过程可以用一些手法实现。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 31;
const int inf = 1931931931;
int n,q,a[N],f[N],p[N],v[N],nxt[N],ans[N],g[N];
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;i++)
scanf("%d ",&a[i]);
int k = 0;
memset(f,127,sizeof(f));
for(int i = 1;i <= n;i++){
v[a[i]] = 1;
while(v[k])
f[k] = i, k++;
nxt[p[a[i]]] = i;
p[a[i]] = i;
}
for(int i = k;i >= 0;i--)
ans[i] = f[i];
f[k + 1] = inf; k++;
for(int i = 1;i <= n;i++){
int tp = a[i];
if(!nxt[i]){
while(tp < k)
ans[tp] = min(ans[tp],f[tp] - i + 1), f[tp++] = inf;
k = a[i];
}
while(tp < k && f[tp] < nxt[i]){
ans[tp] = min(ans[tp],f[tp] - i + 1);
f[tp] = nxt[i]; tp++;
}
}
scanf("%d",&q);
for(int i = 1;i <= n;i++){
g[i] = g[i - 1];
while(i == ans[g[i]])
g[i]++;
}
while(q--){
int x; scanf("%d",&x);
printf("%d\n",g[x]);
}
}
845

被折叠的 条评论
为什么被折叠?



