[海军国际项目办公室]羽未

羽未

题目概述

在这里插入图片描述
在这里插入图片描述

题解

T i w A i r O A O \rm\color{black}{T}\color{red}{iwAirOAO} TiwAirOAO:这不就是一个线段树基础练习题吗

首先,对于一个固定的序列 a a a,我们很容易想到一种贪心地求解思路。
我们将一种颜色最左端的点到最右端的点所覆盖的区间看成这种颜色的区间,显然,区间有交的两种颜色必定都要变成同一种颜色。
我们将区间有交的区间看成一个区间连通块,一个区间联通块的点都要变成一种颜色,显然变成该联通块内点数最多的那种颜色是最优的。
我们可以贪心地将所有区间按左端点排序,然后求出所有的区间连通块以及其的点数最多的颜色。
显然,由于我们每次修改最多会给两个区间造成影响,我们可以暴力将这些区间重排。实际上将全部都sort一遍也不会T
时间复杂度 O ( ∣ C ∣ q ) O\left(|C|q\right) O(Cq),可以有 70 70 70pts。

显然,我们可以用数据结构对上面的过程进行优化。
我们考虑如何维护一个区间连通块。
显然,两个区间联通块区别的地方在于它们相邻处是两个没有共同区间覆盖的点。
我们可以维护哪些相邻的两个点是有共同区间将其覆盖住的。
我们每次颜色区间的改变相当于改变我们区间覆盖的范围。
由于一个点可能同时被多个点覆盖住,不妨维护一个点被多少个区间覆盖住,由于我们每次修改都是进行区间的加减,我们不妨就维护区间内最小值划分整个区间。
显然,一个点不可能被负数个区间所覆盖住,我们要的没有共同区间覆盖的相邻点划分区间,可以看成被 0 0 0个全进所覆盖的点,该值如果有的话一定是最小的。
对于每个被划分的区间,我们需要维护该区间内的最大值。
相当于我们整个在线段树上维护被最小值划分开来区间的最大值和。
这显然就是个线段树的经典问题了,我们只需要多维护一个前后缀划分最大值就行了。

时间复杂度 O ( ( n + q ) log ⁡   n ) O\left((n+q)\log\,n\right) O((n+q)logn)

源码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
#define MAXN 200005
#define mkpr make_pair 
#define pb push_back
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f; 
} 
template<typename _T>
void print(_T x){if(x>9)print(x/10);putchar(x%10+'0');}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int n,q,a[MAXN],b[MAXN<<1],totb,L[MAXN],R[MAXN],val[MAXN],totd;
set<int>s[MAXN];
struct ming{int i,x;}p[MAXN];
struct tann{int l,r,w;}d[MAXN];
struct node{
	int lmx,rmx,maxx,sum,minn;
	node(){lmx=rmx=maxx=sum=minn=0;}
};
node merge(node x,node y){
	node res;res.minn=min(x.minn,y.minn);
	res.maxx=max(x.maxx,y.maxx);
	if(x.minn<y.minn)
		res.lmx=x.lmx,res.rmx=max(x.rmx,y.maxx),res.sum=x.sum;
	else if(x.minn>y.minn)
		res.lmx=max(x.maxx,y.lmx),res.rmx=y.rmx,res.sum=y.sum;
	else res.lmx=x.lmx,res.rmx=y.rmx,res.sum=x.sum+y.sum+max(x.rmx,y.lmx); 
	return res;
}
class SegmentTree{
	private:
		int lzy[MAXN<<2];node tr[MAXN<<2];
		void pushup(int rt){tr[rt]=merge(tr[lson],tr[rson]);}
		void pushdown(int rt){
			if(lzy[rt]){
				lzy[lson]+=lzy[rt];tr[lson].minn+=lzy[rt];
				lzy[rson]+=lzy[rt];tr[rson].minn+=lzy[rt];
				lzy[rt]=0;
			}
		}
	public:
		void modify(int rt,int l,int r,int al,int ar,int aw){
			if(ar<l||al>ar||l>r||al>ar)return ;
			if(al<=l&&r<=ar){lzy[rt]+=aw;tr[rt].minn+=aw;return ;}
			int mid=l+r>>1;pushdown(rt);
			if(al<=mid)modify(lson,l,mid,al,ar,aw);
			if(ar>mid)modify(rson,mid+1,r,al,ar,aw);
			pushup(rt);
		}
		void insert(int rt,int l,int r,int ai,int aw){
			if(l>r||l>ai||r<ai)return ;
			if(l==r){tr[rt].sum=tr[rt].maxx=aw;tr[rt].lmx=tr[rt].rmx=0;return ;}
			int mid=l+r>>1;pushdown(rt);
			if(ai<=mid)insert(lson,l,mid,ai,aw);
			if(ai>mid)insert(rson,mid+1,r,ai,aw);
			pushup(rt);
		}
		int query(){return (tr[1].minn==0)*(tr[1].lmx+tr[1].rmx+tr[1].sum);}
}T;
int main(){
	freopen("umi.in","r",stdin);
	freopen("umi.out","w",stdout);
	read(n);read(q);
	for(int i=1;i<=n;i++)read(a[i]),b[++totb]=a[i];
	for(int i=1;i<=q;i++)read(p[i].i),read(p[i].x),b[++totb]=p[i].x;
	sort(b+1,b+totb+1);totb=unique(b+1,b+totb+1)-b-1;
	for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+totb+1,a[i])-b;
	for(int i=1;i<=q;i++)p[i].x=lower_bound(b+1,b+totb+1,p[i].x)-b;
	for(int i=1;i<=n;i++)s[a[i]].insert(i),val[a[i]]++;
	for(int i=1;i<=totb;i++)if(val[i])L[i]=*s[i].begin(),R[i]=*s[i].rbegin();
	for(int i=1;i<=totb;i++)if(val[i])T.modify(1,1,n,L[i],R[i]-1,1),T.insert(1,1,n,L[i],val[i]);
	printf("%d\n",n-T.query());
	for(int i=1;i<=q;i++){
		int u=a[p[i].i],v=p[i].x;s[u].erase(p[i].i);
		if(val[u])T.insert(1,1,n,L[u],0);val[u]--;
		if(L[u]==p[i].i||R[u]==p[i].i){
			if(val[u]>=0)T.modify(1,1,n,L[u],R[u]-1,-1);
			if(val[u])L[u]=*s[u].begin(),R[u]=*s[u].rbegin(),
				T.modify(1,1,n,L[u],R[u]-1,1);
		}
		if(val[u])T.insert(1,1,n,L[u],val[u]);
		if(val[v])T.insert(1,1,n,L[v],0); 
		s[v].insert(p[i].i);val[v]++;
		if(val[v]==1||L[v]>p[i].i||R[v]<p[i].i){
			if(val[v]>1)T.modify(1,1,n,L[v],R[v]-1,-1);
			if(val[v])L[v]=*s[v].begin(),R[v]=*s[v].rbegin(),
				T.modify(1,1,n,L[v],R[v]-1,1);
		}
		if(val[v])T.insert(1,1,n,L[v],val[v]);
		printf("%d\n",n-T.query());a[p[i].i]=v;
	}
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值