题目大意
一个序列是好的,当且仅当,若两个元素相等,则它们之间的所有元素都相等,比如
[
3
,
3
,
3
,
4
,
1
,
1
]
[3,3,3,4,1,1]
[3,3,3,4,1,1]。
现在有一个初始序列
a
1
,
⋯
,
a
n
a_1,\cdots,a_n
a1,⋯,an,你要把它修改成好的。如果你把一个值为
x
x
x 的元素改成
y
y
y,那么所有值为
x
x
x 的元素都要改成
y
y
y。求最少需要修改多少个位置。
在 hard version 中,还有
q
q
q 次单点修改,每次修改都要回答一次。(在 easy version 中,
q
=
0
q=0
q=0)
1
≤
n
,
a
i
≤
2
×
1
0
5
,
0
≤
q
≤
2
×
1
0
5
1 \leq n,a_i \leq 2 \times 10^5,~0 \leq q \leq 2 \times 10^5
1≤n,ai≤2×105, 0≤q≤2×105
5s
\\
\\
\\
easy version
设数字
x
x
x 最早出现在
l
l
l,最晚出现在
r
r
r,那么最终
[
l
,
r
]
[l,r]
[l,r] 全部都要相同。
那么每个数字相当于给出一个区间,这些区间形成了若干个不相交的区间并,每个区间并内全部要相同。
那么最优的肯定是每个区间并内选择众数保留。答案就是
n
n
n 减去各众数的出现次数和。
用个栈+并查集就可以求出来了。
hard version
感谢大佬的博客让我读懂了题解 qaq
现在是要想办法维护这个区间并,以及区间并内的众数。
一个非常好的办法是,对于数字 x x x,假设它的出现区间是 [ l , r ] [l,r] [l,r],给 [ l , r − 1 ] [l,r-1] [l,r−1] 全体加 1 1 1,那么 0 0 0 就是区间并的分界点。
因此用一个线段树维护区间并的情况。每个区间记录最小值、最小值的最左位置
l
l
l、最小值的最右位置
r
r
r、
[
l
+
1
,
r
]
[l+1,r]
[l+1,r] 的各众数的出现次数和
s
u
m
sum
sum(去掉开头和结尾的连续段是为了方便合并),记为
{
m
i
n
v
a
l
,
l
,
r
,
s
u
m
}
\{minval,l,r,sum\}
{minval,l,r,sum}。如果最小值为
0
0
0,那么
s
u
m
sum
sum 就是要求的东西了。
合并两个区间的话,首先是取最小值较小的那一边(因为如果最小值不同那么大的那边肯定没有
0
0
0),如果两边最小值相同,则是
{
m
i
n
v
a
l
,
l
l
s
o
n
,
r
r
s
o
n
,
s
u
m
l
s
o
n
+
s
u
m
r
s
o
n
+
s
(
r
l
s
o
n
+
1
,
l
r
s
o
n
)
}
\{minval,l_{lson},r_{rson},sum_{lson}+sum_{rson}+s(r_{lson}+1,l_{rson})\}
{minval,llson,rrson,sumlson+sumrson+s(rlson+1,lrson)},其中
s
(
l
,
r
)
s(l,r)
s(l,r) 表示
[
l
,
r
]
[l,r]
[l,r] 的众数的出现次数。
所以接下来的问题就是 s ( l , r ) s(l,r) s(l,r) 怎么求。普通的求区间众数是很难做的,但是这题有个性质,就是如果我问 [ l , r ] [l,r] [l,r] 的区间众数,那么这个众数肯定全部落在 [ l , r ] [l,r] [l,r] 之中,不会在区间外。因此,再开一棵线段树,在每个数 x x x 的最左位置赋值为 x x x 的出现次数,那么 s ( l , r ) s(l,r) s(l,r) 实际上就是个区间最大值。
参考
https://blog.csdn.net/qq_39972971/article/details/100884760
代码
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2e5+5;
struct TR{
int sum,l0,r0,nmin;
};
int n,q,a[maxn],maxa;
set<int> pos[maxn];
int nmax[4*maxn];
void nmax_xg(int k,int l,int r,int x,int z)
{
if (l==r)
{
nmax[k]+=z;
return;
}
int t=k<<1, mid=(l+r)>>1;
if (x<=mid) nmax_xg(t,l,mid,x,z); else nmax_xg(t+1,mid+1,r,x,z);
nmax[k]=max(nmax[t],nmax[t+1]);
}
int nmax_cx(int k,int l,int r,int x,int y)
{
if (x>y) return 0;
if (x<=l && r<=y) return nmax[k];
int t=k<<1, mid=(l+r)>>1, re=0;
if (x<=mid) re=max(re,nmax_cx(t,l,mid,x,y));
if (mid<y) re=max(re,nmax_cx(t+1,mid+1,r,x,y));
return re;
}
TR tr[4*maxn];
int bz[4*maxn];
void update(int k,int t)
{
tr[t].nmin+=bz[k], tr[t+1].nmin+=bz[k];
bz[t]+=bz[k], bz[t+1]+=bz[k];
bz[k]=0;
}
TR merge(const TR &a,const TR &b)
{
if (a.nmin<b.nmin) return a;
else if (a.nmin>b.nmin) return b;
else return (TR){a.sum+b.sum+nmax_cx(1,1,n,a.r0+1,b.l0),a.l0,b.r0,a.nmin};
}
void tr_js(int k,int l,int r)
{
if (l==r)
{
tr[k].l0=tr[k].r0=l;
return;
}
int t=k<<1, mid=(l+r)>>1;
tr_js(t,l,mid), tr_js(t+1,mid+1,r);
tr[k]=merge(tr[t],tr[t+1]);
}
void tr_xg_sing(int k,int l,int r,int x,int z)
{
if (l==r)
{
nmax_xg(1,1,n,x,z);
return;
}
int t=k<<1, mid=(l+r)>>1;
update(k,t);
if (x<=mid) tr_xg_sing(t,l,mid,x,z); else tr_xg_sing(t+1,mid+1,r,x,z);
tr[k]=merge(tr[t],tr[t+1]);
}
void tr_xg_q(int k,int l,int r,int x,int y,int z)
{
if (x>y) return;
if (x<=l && r<=y)
{
tr[k].nmin+=z;
bz[k]+=z;
return;
}
int t=k<<1, mid=(l+r)>>1;
update(k,t);
if (x<=mid) tr_xg_q(t,l,mid,x,y,z);
if (mid<y) tr_xg_q(t+1,mid+1,r,x,y,z);
tr[k]=merge(tr[t],tr[t+1]);
}
void modify(int x,int sig)
{
if (pos[x].empty()) return;
tr_xg_sing(1,0,n,*pos[x].begin(),sig*pos[x].size());
set<int>::iterator it=pos[x].end();
it--;
tr_xg_q(1,0,n,*pos[x].begin(),*it-1,sig);
}
void calc_ans()
{
int ans=n-tr[1].sum-nmax_cx(1,1,n,1,tr[1].l0)-nmax_cx(1,1,n,tr[1].r0+1,n);
printf("%d\n",ans);
}
int main()
{
scanf("%d %d",&n,&q);
fo(i,1,n)
{
scanf("%d",&a[i]);
maxa=max(maxa,a[i]);
pos[a[i]].insert(i);
}
tr_js(1,0,n);
fo(i,1,maxa) modify(i,1);
calc_ans();
while (q--)
{
int x,y;
scanf("%d %d",&x,&y);
modify(a[x],-1);
pos[a[x]].erase(x);
modify(a[x],1);
modify(y,-1);
pos[y].insert(x);
modify(y,1);
a[x]=y;
calc_ans();
}
}