数颜色--用主席树的不用再白费功夫了,已踩坑

题目链接:

数颜色 - 洛谷

题意:

给定n只兔子和其颜色,接下来给m次行为,行为有两种,op=1时给定l,r,c,查询[l,r]区间内颜色为c的兔子有多少只,op=2时给定x,表示将位置x和x+1位置的两只兔子进行交换。(n<=3e5,1<=l,r,c,x<=n)

样例

输入

6 5 
1 2 3 2 3 3  
1 1 3 2 
1 4 6 3  
2 3 
1 1 3 2  
1 4 6 3

输出

1 
2 
2 
3 

思路:

一看是查询[l,r]区间满足某种条件的数量问题,直接就把主席树掏出来了,结果这题卡常卡的死死的,不过也没算白写,最起码让我知道了对主席树进行修改是依旧是通过insert的方式从旧版本更新而来,通过代码中注释掉的那部分来更新是不行的。

根据题目给定的数据的范围,我们可以将第c种颜色的兔子出现的位置存在一个vector vc[c]中,对于查询[l,r]中有多少只颜色为c的兔子,我们就通过lower_bound查询vc[c]中第一个大于等于l的位置,用upper_bound查询vc[c]中以一个大于r的位置,两者相减即[l,r]中c颜色兔子的数量。

对于将x和x+1处的兔子交换,那就更简单了,假设x和x+1处的兔子的颜色分别为c1和c2,那就从vc[c1]中找到x所在的位置,将该位置的数从x换成x+1就行了,同样从vc[c2]中找到x+1所在的位置,将该位置的数从x+1换成x就行了,这里最开始用迭代器进行更改时还出错了,错误代码我下面也给了,原因是x和x+1位置的兔子可能是一种颜色,进行下面的错误修改后对应改颜色的vc中x,x+1变成了x+1,x,不再满足递增,在后面查询的时候不满足二分的单调性了。

这样是错的

auto iter1=lower_bound(vc[a[x]].begin(),vc[a[x]].end(),x);
auto iter2=lower_bound(vc[a[x+1]].begin(),vc[a[x+1]].end(),x+1);
*iter1=x+1;
*iter2=x;

这样就是对的

auto iter1=lower_bound(vc[a[x]].begin(),vc[a[x]].end(),x);
*iter1=x+1;
auto iter2=lower_bound(vc[a[x+1]].begin(),vc[a[x+1]].end(),x+1);
*iter2=x;

以后建议以后用迭代器就是迭代器查完接着用,不要攒着一块用 

代码:

TLE的主席树代码

#include <bits/stdc++.h>

using namespace std;

const int N=3e5+10;
const int M=N*100;
int n,m;
int lch[M],rch[M],sum[M];
int root[N],tot=0;
int a[N];

void cpy(int from,int to){
	lch[to]=lch[from];
	rch[to]=rch[from];
	sum[to]=sum[from];
}
void insert(int &u,int old,int L,int R,int x,int k){
	u=++tot;
	cpy(old,u);
	sum[u]+=k;
	if(L==R)
		return;
	int mid=(L+R)>>1;
	if(x<=mid)
		insert(lch[u],lch[u],L,mid,x,k);
	else
		insert(rch[u],rch[u],mid+1,R,x,k);
}
//void modify(int u,int L,int R,int x,int k){
//	sum[u]+=k;
//	if(L==R)
//		return;
//	int mid=(L+R)>>1;
//	if(x<=mid)
//		modify(lch[u],L,mid,x,k);
//	else
//		modify(rch[u],mid+1,R,x,k);
//}
int query(int s,int t,int L,int R,int x){
	if(L==R)
		return sum[t]-sum[s];
	int mid=(L+R)>>1;
	if(x<=mid)
		return query(lch[s],lch[t],L,mid,x);
	else
		return query(rch[s],rch[t],mid+1,R,x);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		insert(root[i],root[i-1],1,N,a[i],1);
	}
	int op,l,r,c,x;
	while(m--){
		scanf("%d",&op);
		if(op==1){
			scanf("%d%d%d",&l,&r,&c);
			printf("%d\n",query(root[l-1],root[r],1,N,c));
		}
		else{
			scanf("%d",&x);
//			modify(root[x],1,N,a[x],-1);
//			modify(root[x],1,N,a[x+1],1);
			insert(root[x],root[x-1],1,N,a[x+1],1);
			insert(root[x+1],root[x],1,N,a[x],1);
			swap(a[x],a[x+1]);
		}
	}

}

AC代码 

#include <bits/stdc++.h>

using namespace std;

const int N=3e5+10;
vector<int> vc[N];
int a[N];
int n,m;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		vc[a[i]].push_back(i);
	}
	int op,l,r,c,x;
	int pos1,pos2;
	while(m--){
		scanf("%d",&op);
		if(op==1){
			scanf("%d%d%d",&l,&r,&c);
			pos1=lower_bound(vc[c].begin(),vc[c].end(),l)-vc[c].begin();
			pos2=upper_bound(vc[c].begin(),vc[c].end(),r)-vc[c].begin();
			printf("%d\n",pos2-pos1);
		}
		else{
			scanf("%d",&x);
			int c1=a[x],c2=a[x+1];
			pos1=lower_bound(vc[c1].begin(),vc[c1].end(),x)-vc[c1].begin();
			vc[c1][pos1]=x+1;
			pos2=lower_bound(vc[c2].begin(),vc[c2].end(),x+1)-vc[c2].begin();
			vc[c2][pos2]=x;
			swap(a[x],a[x+1]);
		}
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值