【CF1209G1+G2】Into Blocks (easy+hard) 题解

题目大意

  一个序列是好的,当且仅当,若两个元素相等,则它们之间的所有元素都相等,比如 $[3,3,3,4,1,1]$。
  现在有一个初始序列 $a_1,\cdots,a_n$,你要把它修改成好的。如果你把一个值为 $x$ 的元素改成 $y$,那么所有值为 $x$ 的元素都要改成 $y$。求最少需要修改多少个位置。
  在 hard version 中,还有 $q$ 次单点修改,每次修改都要回答一次。(在 easy version 中,$q=0$)

  $1 \leq n,a_i \leq 2 \times 10^5,~0 \leq q \leq 2 \times 10^5$
  5s

easy version

  设数字 $x$ 最早出现在 $l$,最晚出现在 $r$,那么最终 $[l,r]$ 全部都要相同。
  那么每个数字相当于给出一个区间,这些区间形成了若干个不相交的区间并,每个区间并内全部要相同。
  那么最优的肯定是每个区间并内选择众数保留。答案就是 $n$ 减去各众数的出现次数和。

  用个栈+并查集就可以求出来了。

hard version

  感谢大佬的博客让我读懂了题解 qaq

  现在是要想办法维护这个区间并,以及区间并内的众数。

  一个非常好的办法是,对于数字 $x$,假设它的出现区间是 $[l,r]$,给 $[l,r-1]$ 全体加 $1$,那么 $0$ 就是区间并的分界点。

  因此用一个线段树维护区间并的情况。每个区间记录最小值、最小值的最左位置 $l$、最小值的最右位置 $r$、$[l+1,r]$ 的各众数的出现次数和 $sum$(去掉开头和结尾的连续段是为了方便合并),记为 $\{minval,l,r,sum\}$。如果最小值为 $0$,那么 $sum$ 就是要求的东西了。
  合并两个区间的话,首先是取最小值较小的那一边(因为如果最小值不同那么大的那边肯定没有 $0$),如果两边最小值相同,则是 $\{minval,l_{lson},r_{rson},sum_{lson}+sum_{rson}+s(r_{lson}+1,l_{rson})\}$,其中 $s(l,r)$ 表示 $[l,r]$ 的众数的出现次数。

  所以接下来的问题就是 $s(l,r)$ 怎么求。普通的求区间众数是很难做的,但是这题有个性质,就是如果我问 $[l,r]$ 的区间众数,那么这个众数肯定全部落在 $[l,r]$ 之中,不会在区间外。因此,再开一棵线段树,在每个数 $x$ 的最左位置赋值为 $x$ 的出现次数,那么 $s(l,r)$ 实际上就是个区间最大值。

参考

  https://blog.csdn.net/qq_39972971/article/details/100884760

代码

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#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();
}
}