【BZOJ 2120】【国家集训队 2011】【数颜色】

首先这题暴力可过,没事干就别写正解。。。

正解是三维带修改莫队,在普通莫队基础上再加上一个时间维,作为第三关键字参与排序,但是时间的转移十分蛋痛。。。

如果时间转移的下标在[L,R]内,就要对ans进行维护,同时对数组修改。如果不在,就只对数组修改。。。

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
struct hp{
	int st,l,r,tim,num;
}qst[100001];
int L,R,TIM,n,m,ans=0,a[100001],b[200002],c[100001];
int num[100001],change[100001][3],size,ansi[100001];
int cmp(const hp &a,const hp &b)
{
	if ((a.st<b.st)||(a.st==b.st&&a.r<b.r)||(a.st==b.st&&b.r==a.r&&a.tim<b.tim))
	  return 1;
	else return 0;
}
void work(int i)
{
	int l=qst[i].l,r=qst[i].r,tim=qst[i].tim;
    if (l!=r)
      {
	    while (L<l)
	      {
	        if (num[a[L]]==1) ans--;
	        num[a[L]]--;
	        L++;
	      }
	    while (L>l)
	      {
	        L--;
	        if (!num[a[L]]) ans++;
	        num[a[L]]++;
	      }
	    while (R<r)
	      {
	        R++;
	        if (!num[a[R]]) ans++;
	        num[a[R]]++;
	      }
	    while (R>r)
	      {
	        if (num[a[R]]==1) ans--;
	        num[a[R]]--;
	        R--;
	      }
      }
    while (tim>TIM)
      {
      	TIM++;
      	if (L<=change[TIM][0]&&change[TIM][0]<=R)
      	  {
      	    num[change[TIM][1]]--;
      	    if (num[change[TIM][1]]==0) ans--;
      	    num[change[TIM][2]]++;
      	    if (num[change[TIM][2]]==1) ans++;
      	  }
        a[change[TIM][0]]=change[TIM][2]; 
      }
    while (tim<TIM)
      {
        if (L<=change[TIM][0]&&change[TIM][0]<=R)
	      {
	        num[change[TIM][2]]--;
	        if (num[change[TIM][2]]==0) ans--;
	        num[change[TIM][1]]++;
	        if (num[change[TIM][1]]==1) ans++;
	      }
        a[change[TIM][0]]=change[TIM][1];
        TIM--;
      }
    if (qst[i].num) ansi[qst[i].num]=ans;
}
int main()
{
	int i,per,numi=0,time=0; 
	char opt;
	scanf("%d%d",&n,&m); per=sqrt(n);
	for (i=1;i<=n;++i)
	  {
	    scanf("%d",&a[i]);
	    c[i]=b[i]=a[i];
      }
    b[0]=n;
	for (i=1;i<=m;++i)
	  {
	    scanf("%c",&opt);
	    while (opt!='Q'&&opt!='R')
		  scanf("%c",&opt); 
	    if (opt=='Q')
	      {
	        scanf("%d%d",&qst[i].l,&qst[i].r);
	        qst[i].st=qst[i].l/per+1; 
	        qst[i].num=++numi; qst[i].tim=time;
	      }
	    if (opt=='R')
	      {
	      	time++;
	        scanf("%d%d",&qst[i].l,&change[time][2]);
	        qst[i].r=qst[i].l; qst[i].st=qst[i].l/per+1;
	        qst[i].tim=time; qst[i].num=0;   
			change[time][0]=qst[i].l; change[time][1]=c[qst[i].l]; c[qst[i].l]=change[time][2];
			b[++b[0]]=change[time][2]; 
	      }
	  }
	sort(b+1,b+b[0]+1);
	size=unique(b+1,b+b[0]+1)-b-1;
	for (i=1;i<=n;++i)
	  a[i]=upper_bound(b+1,b+size+1,a[i])-b-1;
	for (i=1;i<=time;++i)
	  {
	  	change[i][1]=upper_bound(b+1,b+size+1,change[i][1])-b-1;
	    change[i][2]=upper_bound(b+1,b+size+1,change[i][2])-b-1;
      }
	sort(qst+1,qst+m+1,cmp);
	
	L=qst[1].l; R=qst[1].r; 
	for (TIM=1;TIM<=qst[1].tim;++TIM)
	  a[change[TIM][0]]=change[TIM][2];  
	for (i=qst[1].l;i<=qst[1].r;++i)
	  {
	    if (num[a[i]]==0) ans++; 
		num[a[i]]++; 
	  }
	if (qst[1].num) ansi[qst[1].num]=ans;
	
	TIM=qst[1].tim;
	for (i=1;i<=m;++i)
	  work(i);
	for (i=1;i<=numi;++i)
	  printf("%d\n",ansi[i]);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值