#loj3094 [BJOI2019]删数

不妨换个想法,构造出一个可以被删光的数列,最大化这个数列和原来数列相同的元素个数

显然一个可以被删光的数列可以分成几段相同的数字,并且每段数字的值刚好等于小于等于这种数字的元素个数

那么可以想出一个naive的dp做法,去dp这些段,

转移方程为

\[dp(i)=max(dp(j)+min(i-j,cnt(i))) \]

这个方程是有组合意义的,数轴上有若干个形如\((i-cnt(i)+1,i)\)的区间

那么\(dp(i)\)代表的就是\((1,i)\)这段区间中线段的并长度

既然是线段求并显然可以转化为区间加然后数0的个数

线段树维护区间最小值及其个数即可了

而操作2相当于更改询问的区间,这个也很简单

注意一件事情,如果一个线段的开头不在询问区间当中,这个线段会被完全删除

在询问区间左移或者右移的时候记得删除这些线段就行了

#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;const int N=7*1e5+10;typedef long long ll;
map <int,int> mp;
struct linetree
{
	int mi[N<<2];int micnt[N<<2];int add[N<<2];
	inline void pd(int p,int p1,int p2)
	{
		if(add[p])
			mi[p1]+=add[p],mi[p2]+=add[p],
			add[p1]+=add[p],add[p2]+=add[p],add[p]=0;
	}
	inline void mg(int p,int p1,int p2)
	{
		if(mi[p1]==mi[p2])
			mi[p]=mi[p1],micnt[p]=micnt[p1]+micnt[p2];
		else if(mi[p1]<mi[p2])
			mi[p]=mi[p1],micnt[p]=micnt[p1];
		else
			mi[p]=mi[p2],micnt[p]=micnt[p2];
	}
	inline void build(int p,int l,int r)
	{
	//	printf("build %d %d %d\n",p,l,r);
		if(r-l==1){mi[p]=mp[r],micnt[p]=1;return;}
		int mid=(l+r)/2;
		build(p<<1,l,mid);build(p<<1|1,mid,r);
		mg(p,p<<1,p<<1|1);
	}
	inline void stadd(int p,int l,int r,int dl,int dr,int pl)
	{
		if(dl==l&&dr==r)
		{
			add[p]+=pl;mi[p]+=pl;return;
		}int mid=(l+r)/2;
		pd(p,p<<1,p<<1|1);
		if(dl<mid)stadd(p<<1,l,mid,dl,min(dr,mid),pl);
		if(mid<dr)stadd(p<<1|1,mid,r,max(dl,mid),dr,pl);
		mg(p,p<<1,p<<1|1);
	}
	inline void qry(int p,int l,int r,int dl,int dr)
	{
		if(dl==l&&dr==r){mg(0,0,p);return;}
		int mid=(l+r)/2;
		pd(p,p<<1,p<<1|1);
		if(dl<mid)qry(p<<1,l,mid,dl,min(dr,mid));
		if(mid<dr)qry(p<<1|1,mid,r,max(dl,mid),dr);
	}
	inline int cqry(int l,int r,int dl,int dr)
	{
	//	printf("cqry %d %d %d %d\n",l,r,dl,dr);
		mi[0]=0x3f3f3f3f;micnt[0]=0;
		qry(1,l,r,dl,dr);return micnt[0]*(mi[0]==0);
	}
}lt;int RGL;int RGR;int we[N];int QL;int QR;
map <int,int> cnt;int n;int m;int add;
inline void inc(int val)
{
	
	int pos=val-(++cnt[val])+1;
	if(val<=QR)
	{
		//printf("inc %d\n",val);
		lt.stadd(1,RGL,RGR,pos-1,pos,1);
	}
}
inline void dec(int val)
{
	
	int pos=val-(cnt[val]--)+1;
//	printf("dec %d,pos=%d",val,pos);
	if(val<=QR)
	{
		//printf("dec %d\n",val);
		lt.stadd(1,RGL,RGR,pos-1,pos,-1);
	}
}
inline void ins(int val)
{
//	printf("ins %d %d\n",val-cnt[val],val);
	if(cnt[val])lt.stadd(1,RGL,RGR,val-cnt[val],val,1);
}
inline void del(int val)
{
//	printf("del %d %d\n",val-cnt[val],val);
	if(cnt[val])lt.stadd(1,RGL,RGR,val-cnt[val],val,-1);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&we[i]);
	for(int i=1;i<=n;i++)cnt[we[i]]++;
	for(map <int,int> :: iterator it=cnt.begin();it!=cnt.end();++it)
		for(int j=it->first,k=1;k<=it->second;j--,k++)mp[j]++;
	RGL=-n-m;RGR=n+m;
	QL=0;QR=n;
	lt.build(1,RGL,RGR);
	for(int i=1,p;i<=m;i++)
	{
		scanf("%d",&p);
		if(p>0)
			dec(we[p]),scanf("%d",&we[p]),we[p]-=add,inc(we[p]);
		else
		{
			scanf("%d",&p);add+=p;
			if(p==-1)
				QR++,QL++,ins(QR);
			else
				del(QR),QR--,QL--;
		}
	//	printf("qry %d %d\n",QL,QR);
		printf("%d\n",lt.cqry(RGL,RGR,QL,QR));
	}return 0;
}
posted @ 2019-05-05 17:35  sweetphoenix  阅读(278)  评论(0编辑  收藏  举报