洛谷P3939填颜色

题目链接:

点击打开链接

看到题第一眼,线段树?树状数组?主席树(蒟蒻不会)全冒出来?

可是仔细看题发现这是道水题

我们先把每种颜色读入

预处理出每种颜色所在位置的排序

拿样例举例:

1 2 3 2 3 3 
我们第一次初始化为b数组:

第一种颜色下标有:1

第二种颜色下标有:2,4

第三种颜色下标有:3,5,6

故b数组为1,2,4,3,5,6

用另两个数组记录每种颜色的开头和结尾

对于查询操作我们考虑二分:找到第一个比l大的数的下标L,找到最后一个比r小的数的下标R,R-L+1即为所求.

对于修改操作:

我们发现它们的相对位置是不变的:

拿上面的a数组样例2,3交换说明:

1,当这两种颜色不同时

容易证明这两个数在原来各自颜色的相对位置是不变的,所以直接暴力修改a数组就可以了

2, 当这两种颜色相同时改和不改一样,所以不用操作

上代码

---------------------------------------------------------------------------------分割线------------------------------------------------------------------------------------------------------------------------------------------


#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int N=300010;
int n,m,st[N],en[N],tot[N],tot1[N];
int a[N],xu[N],b[N];

void pre()
{
	int i,j,k,last=0;
	for(i=1;i<N;++i)
	  if(tot[i]!=0)
	  {
	   st[i]=last+1,en[i]=st[i]+tot[i]-1;
       last=en[i];tot1[i]=st[i]-1;
	  }
	for(i=1;i<=n;++i)
	  b[++tot1[a[i]]]=i,xu[i]=tot1[a[i]];
}

void find(int l,int r,int c)
{
	int i,j,ans,ans1,mid;
    ans=0;ans1=0;
	i=st[c];j=en[c];
	while(i<=j)
	{
	 mid=(i+j)/2;
	 if(b[mid]>=l) j=mid-1,ans1=mid;
	  else i=mid+1;
	}
	i=st[c];j=en[c];
	while(i<=j)
	{
	 mid=(i+j)/2;
	 if(b[mid]<=r) i=mid+1,ans=mid;
	  else j=mid-1;
	}
	if(ans!=0&&ans1!=0)
	  printf("%d\n",ans-ans1+1);
	else printf("0\n");
}

void work()
{
	int i,j,k,t,t1,t2,t3;
	for(i=1;i<=m;++i)
	{
	  scanf("%d",&j);
      if(j==2)
	  { 
	   scanf("%d",&k);
	    t=xu[k];t1=xu[k+1];
		if(a[k]!=a[k+1])
	    {
	    b[t1]--;b[t]++;
	    t2=xu[k];xu[k]=xu[k+1];xu[k+1]=t2;
		t2=a[k];a[k]=a[k+1];a[k+1]=t2;
	    }
	  }
	  else
	  { 
	    scanf("%d%d%d",&t,&t1,&t2);
		find(t,t1,t2);
	  }
	}
}

int main()
{
    freopen("csy.in","r",stdin);
    freopen("csy.out","w",stdout);
	int i,j,k;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i)
	{
	 scanf("%d",&a[i]);
	 tot[a[i]]++;
	}
	pre();
	work();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值