题目链接:点击这里
题目大意:
给出一个长度为
n
n
n 的序列,每个点的值
v
a
l
∈
[
1
,
m
]
val \in [1,m]
val∈[1,m] ,题目保证每个值都出现过,对于每一个
i
(
1
≤
i
≤
m
)
i(1\le i\le m)
i(1≤i≤m) 求包含
1
1
1 到
n
n
n 所有数的区间的最小长度
题目分析:
同样的思路,我们想考虑一个暴力的
d
p
dp
dp :
设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示以
i
i
i 为起点包含
1
1
1 到
j
j
j 所有数字的最短长度,设
j
j
j 出现的位置依次为
p
o
s
1
,
p
o
s
2
,
.
.
.
,
p
o
s
k
pos_1,pos_2,...,pos_k
pos1,pos2,...,posk ,那么在
[
p
o
s
j
−
1
,
p
o
s
j
]
[pos_{j-1},pos_j]
[posj−1,posj] 这一段有转移方程:
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
−
1
]
,
l
a
s
t
[
j
]
)
dp[i][j]=max(dp[i][j-1],last[j])
dp[i][j]=max(dp[i][j−1],last[j])
最后对于
[
p
o
s
k
+
1
,
n
]
[pos_k+1,n]
[posk+1,n] 的
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 应该都赋值为
i
n
f
inf
inf
我们考虑有线段树优化这个式子的转移:
我们由
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 的意义容易知道其是关于
i
i
i 单调不下降的,这样在
[
p
o
s
j
−
1
+
1
,
p
o
s
j
]
[pos_{j-1}+1,pos_j]
[posj−1+1,posj] 这一段上,对
d
p
[
i
]
[
j
]
≤
p
j
dp[i][j]\le p_j
dp[i][j]≤pj 的一段可以直接用线段树区间修改为
p
o
s
j
pos_j
posj ,接下来考虑如何维护出
d
p
[
i
]
[
j
]
−
i
+
1
dp[i][j]-i+1
dp[i][j]−i+1 取其最小值即为所求答案
找
d
p
[
i
]
[
j
]
≤
p
j
dp[i][j]\le p_j
dp[i][j]≤pj 这一段可以利用线段树本来就是二叉树的性质进行二分查找来找到想要修改的区间
维护
d
p
[
i
]
[
j
]
−
i
+
1
dp[i][j]-i+1
dp[i][j]−i+1 的最小值是这个题最具技巧性的部分:
假设当前我们要维护区间
[
l
,
r
]
[l,r]
[l,r] ,设这段区间维护的答案是
r
e
s
res
res ,则有:
r
e
s
=
m
i
n
i
=
l
r
d
p
[
i
]
[
j
]
−
i
+
1
res=min_{i=l}^rdp[i][j]-i+1
res=mini=lrdp[i][j]−i+1
=
l
a
z
y
−
r
+
1
=lazy-r+1
=lazy−r+1
其中
l
a
z
y
lazy
lazy 就是最后一次对区间
[
l
,
r
]
[l,r]
[l,r] 的赋值(不得不说这个转化真的精妙)
具体细节见代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
int read()
{
int res = 0,flag = 1;
char ch = getchar();
while(ch<'0' || ch>'9')
{
if(ch == '-') flag = -1;
ch = getchar();
}
while(ch>='0' && ch<='9')
{
res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
ch = getchar();
}
return res*flag;
}
const int maxn = 2e5+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
struct sgt{
int val,res,r,lazy;
}a[maxn<<2];
vector<int>pos[maxn];
void pushup(int root)
{
a[root].val = min(a[root<<1].val,a[root<<1|1].val);
a[root].res = min(a[root<<1].res,a[root<<1|1].res);
}
void pushdown(int root)
{
if(a[root].lazy)
{
int tmp = a[root].lazy;
a[root].lazy = 0;
a[root<<1].lazy = a[root<<1|1].lazy = tmp;
a[root<<1].val = a[root<<1|1].val = tmp;
a[root<<1].res = tmp-a[root<<1].r+1;
a[root<<1|1].res = tmp-a[root<<1|1].r+1;
}
}
void build(int root,int l,int r)
{
a[root].r = r;a[root].res = inf;
if(l == r) return ;
int mid = l+r>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
}
int find(int root,int l,int r,int ql,int qr)
{
if(a[root].val > qr || l > qr || r < ql) return -1;
if(l == r) return l;
int mid = l+r>>1;
int res = find(root<<1|1,mid+1,r,ql,qr);
if(res == -1) res = find(root<<1,l,mid,ql,qr);
return res;
}
void updat(int root,int l,int r,int ql,int qr,int val)
{
if(l>qr || r<ql) return ;
if(l>=ql && r<=qr)
{
a[root].val = val;
a[root].lazy = val;
a[root].res = val-r+1;
return ;
}
pushdown(root);
int mid = l+r>>1;
updat(root<<1,l,mid,ql,qr,val);
updat(root<<1|1,mid+1,r,ql,qr,val);
pushup(root);
}
int main()
{
int n = read(),m = read();
for(int i = 1;i <= n;i++)
{
int num = read();
pos[num].push_back(i);
}
build(1,1,n);
for(int i = 1;i <= m;i++)
{
int pre = 0;
for(auto now : pos[i])
{
int poss = find(1,1,n,pre+1,now);
if(poss != -1) updat(1,1,n,pre+1,poss,now);
pre = now;
}
if(pos[i].back() < n) updat(1,1,n,pos[i].back()+1,n,inf);
printf("%d%c",a[1].res,i==n ? '\n' : ' ');
}
return 0;
}