链接
感谢 @newbiegcz 的指导
做法和题解是一样的,然而英文太烂没有看懂。看了cz_xuyixuan的才看懂。后面维护方法和他略有不同
要找分界点:分段后,每段包含其中的所有颜色的所有出现位置
首先:一个颜色只考虑其两端 [ L i , R i ] [L_i,R_i] [Li,Ri],分界点不能在这个区间中间。每个颜色会删去在 [ L i , R i ) [L_i,R_i) [Li,Ri)的分界点,剩下的就是合法分界点(被删除次数为 0 0 0的位置后)
分出每一段后,求出每段颜色出现次数最大值, 答 案 = n − 每 段 最 大 值 求 和 答案 = n - 每段最大值求和 答案=n−每段最大值求和
带修改的情况用线段树维护这个覆盖次数(被删除的次数),为 0 0 0则是一个合法的分界点。而注意到在顶层n后面一定是一个合法的分界点。故只需要记录最小值,默认每一层的最小值位置后就是合法分界点。合并的时候讨论一下即可这样就只用一个线段树搞定了!(这种做法学习自@newbiegcz的代码)
对于修改,经典的做法是看成对两种颜色的删除和插入。修改一种颜色时,也是先在线段树中删除这种颜色,再修改,再插入。这样非常好写!
#include
using namespace std;
#define PB push_back
#define lowbit(x) (x&(-x))
#define MP make_pair
#define fi first
#define se second
#define ls(x) (x << 1)
#define rs(x) ((x << 1) | 1)
#define rep(i,l,r) for (int i = l ; i <= r ; i++)
#define down(i,r,l) for (int i = r ; i >= l ; i--)
#define fore(i,x) for (int i = head[x] ; i ; i = e[i].next)
#define SZ(v) (int)v.size()
typedef long long ll;
typedef pair <int,int> pr;
const int maxn = 2e5 + 10;
struct node{
int mn,z,mx,lmx,rmx,ans;
node (){
mn = z = mx = lmx = rmx = ans = 0; }
void print(){
cout<<"mn : "<<mn<<" z : "<<z<<" mx : "<<mx<<" lmx: "<<lmx<<" rmx : "<<rmx<< "ans : "<<ans<<endl;
}
}sgt[maxn << 2];
node operator + (const node &a,const node &b){
node res;
res.mn = min(a.mn,b.mn);
res.mx = max(a.mx,b.mx);
if ( a.mn < b.mn ){
//then we think actually breakpoint is in left subsegment
res.ans = a.ans;
res.lmx = a.lmx , res.rmx = max(a.rmx,b.mx);
}
else if ( a.mn > b.mn ){
res.ans = b.ans;
res.lmx = max(a.mx,b.lmx) , res.rmx = b.rmx;
}
else{
//merge the breakpoints in left and right
res.ans = a.ans + b.ans + max(a.rmx,b.lmx);
res.lmx = a.lmx , res.rmx = b.rmx;
}
//note that when pushup, the lazy tag z must equal to 0, so need to care about it.
return res;
}
void operator += (node &a,int v){
a.mn += v;
a.z += v;
}
int n,q;
int a[maxn + 1];
set <int> st[maxn + 1];
void pushdown(int o){
if ( !sgt[o].z ) return;
sgt[ls(o)] += sgt[o].z;
sgt[rs(o)] += sgt[o].z;
sgt[o].z = 0;
}
void pushup(int o){
sgt[o] = sgt[ls(o)] + sgt[rs(o)];
}
void modify(int o,int l,int r,int L,int R,int v){
if ( L > R ) return;
if ( L <= l && R >= r ) return void(sgt[o] += v);
int mid = (l + r) >> 1;
pushdown(o);
if ( L <= mid ) modify(ls(o),l,mid,L,R,v);
if ( R > mid ) modify(rs(o),mid + 1,r,L,R,v);
pushup(o);
}
void modify(int o,int l,int r,int id,int v){
if ( l == r ) return void(sgt[o].mx = sgt[o].lmx = v); //In the last layer, sgt[o].ans = sgt[o].rmx = 0 by default
int mid = (l + r) >> 1;
pushdown(o);
if ( id <= mid ) modify(ls(o),l,mid,id,v);
else modify(rs(o),mid + 1,r,id,v);
pushup(o);
}
void print(int o,int l,int r){
cout<<"seg : "<<o<<" "<<l<<" "<<r<<" sgt : "; sgt[o].print();
if ( l == r ) return;
int mid = (l + r) >> 1;
pushdown(o);
print(ls(o),l,mid);
print(rs(o),mid + 1,r);
}
void change(const auto &st,int d){
if ( SZ(st) == 0 ) return;
modify(1,1,n,*st.begin(),*st.rbegin() - 1,d);
modify(1,1,n,*st.begin(),d == 1 ? SZ(st) : 0);
//print(1,1,n);
}
int getans(){
return n - sgt[1].ans - sgt[1].lmx - sgt[1].rmx;
}
int main(){
freopen("input.txt","r",stdin);
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n>>q;
rep(i,1,n){
cin>>a[i];
st[a[i]].insert(i);
}
rep(i,1,maxn) change(st[i],1);
cout<<getans()<<"\n";
rep(i,1,q){
int id,x;
cin>>id>>x;
change(st[a[id]],-1);
st[a[id]].erase(id);
change(st[a[id]],1);
a[id] = x;
change(st[a[id]],-1);
st[a[id]].insert(id);
change(st[a[id]],1);
cout<<getans()<<"\n";
}
}
主要的问题是没有想到分界点可以以覆盖次数来判断,开始想的是:所有出现在它之前的颜色的最后一次出现位置都 < = <= <=它, [ x s t . i f L i d < = x R i d < = x ] [x st. if Lid <= x Rid <= x] [xst.ifLid<=xRid<=x] , 但是想到去取max了,没有想到更直白的用每种颜色的区间去删除不合法的分界点