bzoj2120(带修改莫队 或 树状数组套主席树)

做法一:

 

带修改莫队模板

第一关键字:左端点的块

第二:右端点块

第三:前面的修改次数,这里称time

#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=10005;

int n,m;
int block,pos[N],col[N];

int query_num;
struct aa
{
	int l,r,time,id,ans;
	bool operator <(const aa &b)const
	{
		if (pos[l]!=pos[b.l]) return pos[l]<pos[b.l];
		if (pos[r]!=pos[b.r]) return pos[r]<pos[b.r];
		return time<b.time;
	}
}a[N];
bool cmp(aa a,aa b)
{
	return a.id<b.id;
}

int time;
struct bb
{
	int x,y,z;
}q[N];

int tt[N*100],tmp,l,r,now;

void insert(int pos)
{
	tt[col[pos]]++;
	if (tt[col[pos]]==1) tmp++;
}
void del(int pos)
{
	tt[col[pos]]--;
	if (tt[col[pos]]==0) tmp--; 
}

void in_time(int i)
{
	int pos=q[i].x,to=q[i].y,&from=q[i].z;
	if (pos>=l&&pos<=r) del(pos);
	from=col[pos];
	col[pos]=to;
	if (pos>=l&&pos<=r) insert(pos);
}
void out_time(int i)
{
	int pos=q[i].x,to=q[i].y,from=q[i].z;
	if (pos>=l&&pos<=r) del(pos);
	col[pos]=from;
	if (pos>=l&&pos<=r) insert(pos);
}

void work()
{
	l=1,r=0,now=0;
	for (int i=1;i<=query_num;i++)
	{
		for (;r<a[i].r;r++) insert(r+1);
		for (;r>a[i].r;r--) del(r);
		
		for (;l<a[i].l;l++) del(l);
		for (;l>a[i].l;l--) insert(l-1);
		
		for (;now<a[i].time;now++) in_time(now+1);
		for (;now>a[i].time;now--) out_time(now);
		
		a[i].ans=tmp;
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%d",&col[i]);
	block=sqrt(n);
	for (int i=1;i<=n;i++) pos[i]=(i-1)/block+1;
	
	char ch[2];
	int x,y;
	for (int i=1;i<=m;i++)
	{
		scanf("%s",ch);
		scanf("%d%d",&x,&y);
		if (ch[0]=='Q') a[++query_num]=(aa){x,y,time,i};
		else q[++time]=(bb){x,y,0};
	}
	sort(a+1,a+query_num+1);
	work();
	sort(a+1,a+query_num+1,cmp);
	for (int i=1;i<=query_num;i++) printf("%d\n",a[i].ans);
	return 0;
}


 做法二:

如果没有修改就是,处理不重复的基本思路,这个颜色上一个出现位置在l之前。

对于询问l~r,就是询问l~r中多少pre在l之前的。

考虑差分,其实就是在1~r中多少数小于l,主席树模板。

 

但是更改一个颜色就会出现,pre的改变。pre改变用set来维护,有更改就再套上一个树状数组就好。

 

注意:主席树的更改是很暴力的,其实就是暴力增加log 个节点罢了。

 

当然基本思路还是求区间小于等于一个数的个数。

 

还有就是修改一个颜色,会影响当前点的值和他后继的值,那么就是取后继和前驱,删除插入一个数。这用set就可以解决,原先分块写这题,更改这个地方暴力做的,麻烦了

#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
const int N=11005;
const int N2=1000005;

int n,m,c[N];
//
int b[N],nn;
set<int> col[N];
set<int>::iterator it,pre,next;
//
struct Query
{
	char ch[2];
	int x,y;
}Que[N];
/
int tot,rt[N];
struct aa
{
	int lc,rc,l,r,sum;
}a[N*20*20];
void build(int &u,int l,int r)
{
	u=++tot;
	a[u].l=l,a[u].r=r;
	if (l==r) return;
	int mid=(l+r)>>1;
	build(a[u].lc,l,mid);
	build(a[u].rc,mid+1,r);
}
void updata(int old,int &now,int pos,int add)
{
	now=++tot;
	a[now]=a[old];
	a[now].sum+=add;
	if (a[old].l==a[old].r) return;
	int mid=(a[old].l+a[old].r)>>1;
	if (pos<=mid) updata(a[old].lc,a[now].lc,pos,add);
	else updata(a[old].rc,a[now].rc,pos,add);
}
int sum(int u,int t)
{
	if (t==a[u].r) return a[u].sum;
	int mid=(a[u].l+a[u].r)>>1;
	if (t<=mid) return sum(a[u].lc,t);
	return a[a[u].lc].sum+sum(a[u].rc,t);
}
///
inline int lowbit(int x){return x&(-x);}
void updata(int i,int last,int add)
{
	int v;
	for (;i<=n;i+=lowbit(i)) 
	{
		updata(rt[i],v,last,add);
		rt[i]=v;
	}
}
int query(int i,int t)
{
	int ans=0;
	for (;i;i-=lowbit(i)) ans+=sum(rt[i],t);
	return ans;
}
/
int head[N];

void work(int l,int r)
{
	printf("%d\n",query(r,l-1)-query(l-1,l-1));
}

void change(int pos,int now)
{
	now=lower_bound(b+1,b+nn+1,now)-b;
	int old=c[pos];
	
	pre=next=it=col[old].lower_bound(pos);
	pre--;next++;
	updata(pos,*pre,-1);
	if (next!=col[old].end()) updata(*next,pos,-1),updata(*next,*pre,1);
	
	col[old].erase(it);col[now].insert(pos);c[pos]=now;
	
	pre=it=next=col[now].lower_bound(pos);
	pre--;next++;
	updata(pos,*pre,1);
	if (next!=col[now].end()) updata(*next,*pre,-1),updata(*next,pos,1);
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%d",&c[i]),b[++nn]=c[i];
	for (int i=1;i<=m;i++)
	{
		scanf("%s",Que[i].ch);
		scanf("%d%d",&Que[i].x,&Que[i].y);
		if (Que[i].ch[0]=='R') b[++nn]=Que[i].y;
	}
	
	sort(b+1,b+nn+1);
	nn=unique(b+1,b+nn+1)-b-1;
	for (int i=1;i<=n;i++) c[i]=lower_bound(b+1,b+nn+1,c[i])-b;
	for (int i=1;i<=nn;i++) col[i].insert(0);
	
	
	build(rt[0],0,n);
	for (int i=1;i<=n;i++) rt[i]=rt[0];
	
	for (int i=1;i<=n;i++)
	{
		updata(i,head[c[i]],1);
		head[c[i]]=i;
		col[c[i]].insert(i);
	}
	
	for (int i=1;i<=m;i++)
	{
		Query t=Que[i];
		if (t.ch[0]=='Q') work(t.x,t.y);
		else change(t.x,t.y);
	}
	return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值