CF1209G2 Into Blocks (hard version)
看题解过了这道题,理解的时候比较费力,写一篇题解加深印象。
首先考虑 easy vesion,将最少代价转化成最多能留下来多少个,一个区间里能留下来的最大数就是众数个数,显然相等的数要放在一起考虑,容易发现两个区间的众数个数一定大于等于将这两个区间合并后的众数个数,所以按照这样拆成很多个小区间就行了。
code
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
#pragma GCC optimeze(3)
#pragma GCC optimeze(2)
#define PII pair<int, int>
#define pb push_back
#define fi first
#define se second
#define lowbit(x) (x & (-x))
using namespace std;
const int N=2e5+10;
const int M=5e3+5;
const int mod=1e9+7;
double eps=1e-9;
int n,q,a[N],bj[N],maxx,pos[N],ans,x;
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++)cin>>a[i],bj[a[i]]++,pos[a[i]]=i;
for(int i=1;i<=n;){
x=i,maxx=0;
while(i<=x){
maxx=max(maxx,bj[a[i]]),x=max(x,pos[a[i]]),i++;
}
ans+=maxx;
}
cout<<n-ans;
return 0;
}
然后再来考虑带修改的,因为我们分出区间的时候是一直看区间内是否有数没有将与它相等的最后一个数覆盖到,所以有一个 trick:将一个数
那么现在这个序列就被
我们可以再开一个数组
这个区间的最左边和最右边的两个被分割出来的区间不一定是完整的区间,可能会和旁边的区间合并比如:
(不同的字母表示被
所以记录这个区间里面被分割出来的最左边区间和最右边区间各自的
最后要统计答案,所以还要维护这个区间内被分割出来的区间的最大值之和
然后考虑 pushup,
- 左儿子的区间有
,右儿子的区间没有 :
这个时候
- 右儿子的区间有
,左儿子的区间没有 :
和上面的的情况同理。
- 左儿子和右儿子的区间均有
:
这个时候
修改只用维护区间修改
code
#include <bits/stdc++.h>
//#define int long long
#define ull unsigned long long
#pragma GCC optimeze(3)
#pragma GCC optimeze(2)
#define PII pair<int, int>
#define pb push_back
#define fi first
#define se second
#define lowbit(x) (x & (-x))
using namespace std;
const int N=2e5+10;
const int M=17;
const int mod=1e9+7;
double eps=1e-9;
struct node{
int maxx,sum,lv,rv,minn,tag;
}tr[N<<2];
int n,a[N],q;
set<int>s[N];
void pushdown(int p){
if(tr[p].tag){
tr[p<<1].minn+=tr[p].tag,tr[p<<1].tag+=tr[p].tag;
tr[p<<1|1].minn+=tr[p].tag,tr[p<<1|1].tag+=tr[p].tag;
tr[p].tag=0;
}
}
void pushup(int p){
tr[p].minn=min(tr[p<<1].minn,tr[p<<1|1].minn);
tr[p].maxx=max(tr[p<<1].maxx,tr[p<<1|1].maxx);
if(tr[p<<1].minn<tr[p<<1|1].minn){
tr[p].lv=tr[p<<1].lv,tr[p].sum=tr[p<<1].sum,tr[p].rv=max(tr[p<<1|1].maxx,tr[p<<1].rv);
}
if(tr[p<<1].minn>tr[p<<1|1].minn){
tr[p].rv=tr[p<<1|1].rv,tr[p].sum=tr[p<<1|1].sum,tr[p].lv=max(tr[p<<1].maxx,tr[p<<1|1].lv);
}
if(tr[p<<1].minn==tr[p<<1|1].minn){
tr[p].lv=tr[p<<1].lv,tr[p].rv=tr[p<<1|1].rv,tr[p].sum=tr[p<<1].sum+tr[p<<1|1].sum+max(tr[p<<1].rv,tr[p<<1|1].lv);
}
}
void add1(int l,int r,int x,int p,int s,int t){
if(l>r)return;
if(s>=l&&t<=r){
tr[p].minn+=x,tr[p].tag+=x;
return;
}
int mid=(s+t)>>1;
pushdown(p);
if(mid>=l)add1(l,r,x,p<<1,s,mid);
if(mid<r)add1(l,r,x,p<<1|1,mid+1,t);
pushup(p);
}
void add2(int x,int q,int p,int s,int t){
if(s==t){
tr[p].maxx+=q,tr[p].lv+=q;
return;
}
pushdown(p);
int mid=(s+t)>>1;
if(mid>=x)add2(x,q,p<<1,s,mid);
else add2(x,q,p<<1|1,mid+1,t);
pushup(p);
}
void update(int co,int k){
int siz=s[co].size();
if(!siz)return;
add1((*s[co].begin()),*(--s[co].end())-1,k,1,1,n);
add2(*s[co].begin(),k*siz,1,1,n);
}
int ask(){
return n-tr[1].sum-tr[1].lv-tr[1].rv;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>q;int mx=0;
for(int i=1;i<=n;i++){
cin>>a[i],s[a[i]].insert(i),mx=max(mx,a[i]);
}
for(int i=1;i<=mx;i++){if(s[i].size())update(i,1);}
cout<<ask()<<'\n';
while(q--){
int x,y;cin>>x>>y;
int col=a[x];
update(col,-1),s[col].erase(x),update(col,1);
update(y,-1),s[y].insert(x),update(y,1);
a[x]=y;
cout<<ask()<<'\n';
}
return 0;
}
本文作者:Xdik
本文链接:https://www.cnblogs.com/Xdik/p/18708114
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步