【NOI2007T4】项链工厂-线段树+坐标变换

测试地址:项链工厂

做法:之前没想出坐标变换的规律性,于是用Splay做,写了300+行,惨跪......现在回来一想,这不就是道线段树嘛!结果换线段树后只有200行左右,果然线段树还是区间处理的大杀器。

我们设一个坐标的映射序列S,S[i]就是当前标号为i的珠子最原始的标号,我们发现无论怎么旋转、翻转,这个序列都是一个环序列,即一定存在某一个位置X,将其右边的所有元素接到所有元素的左边,形成的序列为{1,2,3,...,N}或者{N,N-1,N-2,...,1}。这样我们就可以用两个参数来表示当前映射序列的状态:一个是当前标号为1的珠子最原始的标号,一个是反映当前的环序列经过切割再接之后的序列是{1,2,3,...,N}还是{N,N-1,N-2,...,1}。于是对于每个旋转操作,改变的是第一个参数,对于每个翻转操作,改变的是第二个参数,修改的时间都是O(1)的。而且,我们可以由这两个参数用O(1)时间算出当前的映射序列上某一个元素的值。这时我们就可以用线段树来维护环上的颜色,这样对于每个询问和修改操作,就可以用O(logn)的时间维护了。

然而还有要注意的小细节,一是在环上连续的一段依靠映射序列映射到原序列上时,并不一定也是连续的一段,可能是由头和尾的两端拼接而成,这时候要特别注意询问的方法以及头和尾的元素是否相等,以免出现计数错误。二是在C操作中,正常情况下询问得到结果后我们会判断头尾两个元素是否相等,如果相等结果减1,但如果整个环只有一个颜色,那么按以上步骤处理完后结果就是0,这不符合要求,所以当询问后得到的结果是1就不用再比较了。

以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
int n,c,a[500010],q,fir=0,lft,rht,ans;
bool fliped=0;
struct smt
{
  int lc,rc,m;
  int p;
}seg[2000010];

void pushdown(int no)
{
  if (seg[no].p)
  {
    seg[no<<1].p=seg[no<<1].lc=seg[no<<1].rc=
	seg[no<<1|1].p=seg[no<<1|1].lc=seg[no<<1|1].rc=seg[no].p;
    seg[no<<1].m=seg[no<<1|1].m=1;
	seg[no].p=0;
  }
}

void pushup(int no)
{
  seg[no].m=seg[no<<1].m+seg[no<<1|1].m;
  seg[no].lc=seg[no<<1].lc,seg[no].rc=seg[no<<1|1].rc;
  if (seg[no<<1].rc==seg[no<<1|1].lc) seg[no].m--;
}

void buildtree(int no,int l,int r)
{
  int mid=(l+r)>>1;
  if (l==r)
  {
    seg[no].lc=seg[no].rc=a[l];
	seg[no].m=1;seg[no].p=0;
	return;
  }
  buildtree(no<<1,l,mid);
  buildtree(no<<1|1,mid+1,r);
  pushup(no);
}

int query(int no,int l,int r,int x)
{
  int mid=(l+r)>>1;
  if (l==r) return seg[no].lc;
  pushdown(no);
  if (x<=mid) return query(no<<1,l,mid,x);
  else return query(no<<1|1,mid+1,r,x);
}

void modify(int no,int l,int r,int s,int t,int x)
{
  int mid=(l+r)>>1;
  if (l>=s&&r<=t)
  {
    seg[no].p=seg[no].lc=seg[no].rc=x;
	seg[no].m=1;
	return;
  }
  pushdown(no);
  if (s<=mid) modify(no<<1,l,mid,s,t,x);
  if (t>mid) modify(no<<1|1,mid+1,r,s,t,x);
  pushup(no);
}

int count(int no,int l,int r,int s,int t)
{
  int mid=(l+r)>>1,sum=0;
  if (l>=s&&r<=t)
  {
    if (!lft) lft=seg[no].lc;
	sum+=seg[no].m;
	if (rht==seg[no].lc) sum--;
	rht=seg[no].rc;
	return sum;
  }
  pushdown(no);
  if (s<=mid) sum+=count(no<<1,l,mid,s,t);
  if (t>mid) sum+=count(no<<1|1,mid+1,r,s,t);
  return sum;
}

int main()
{
  scanf("%d%d",&n,&c);
  for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
  buildtree(1,1,n);
  
  scanf("%d",&q);
  while(q--)
  {
    char op[10];
	int a,b,x;
    scanf("%s",op);
	if (op[0]=='R')
	{
	  scanf("%d",&a);
	  if (!fliped) fir=(fir-a+n)%n;
	  else fir=(fir+a)%n;
	}
	if (op[0]=='F') fliped=!fliped;
	if (op[0]=='S')
	{
	  scanf("%d%d",&a,&b);
	  int posa,posb,ca,cb;
	  if (!fliped)
	  {
	    posa=(fir+a-1)%n+1;
		posb=(fir+b-1)%n+1;
	  }
	  else
	  {
	    posa=(fir-a+1+n)%n+1;
		posb=(fir-b+1+n)%n+1;
	  }
	  ca=query(1,1,n,posa);
	  cb=query(1,1,n,posb);
	  modify(1,1,n,posa,posa,cb);
	  modify(1,1,n,posb,posb,ca);
	}
	if (op[0]=='P')
	{
	  scanf("%d%d%d",&a,&b,&x);
	  int posa,posb;
	  if (!fliped)
	  {
	    posa=(fir+a-1)%n+1;
		posb=(fir+b-1)%n+1;
		if (posa<=posb) modify(1,1,n,posa,posb,x);
		else
		{
		  modify(1,1,n,posa,n,x);
		  modify(1,1,n,1,posb,x);
		}
	  }
	  else
	  {
	    posa=(fir-a+1+n)%n+1;
		posb=(fir-b+1+n)%n+1;
		if (posa>=posb) modify(1,1,n,posb,posa,x);
		else
		{
		  modify(1,1,n,posb,n,x);
		  modify(1,1,n,1,posa,x);
		}
	  }
	}
	if (op[0]=='C'&&op[1]!='S')
	{
	  lft=rht=0;
	  ans=count(1,1,n,1,n);
	  if (lft==rht&&ans>1) ans--;
	  printf("%d\n",ans);
	}
	if (op[0]=='C'&&op[1]=='S')
	{
	  scanf("%d%d",&a,&b);
	  int posa,posb;
	  lft=rht=ans=0;
	  if (a==41&&b==28)
	  {
	    a++;a--;
	  }
	  if (!fliped)
	  {
	    posa=(fir+a-1)%n+1;
		posb=(fir+b-1)%n+1;
		if (posa<=posb) ans=count(1,1,n,posa,posb);
		else
		{
		  int s;
		  ans+=count(1,1,n,posa,n);
		  s=rht;lft=rht=0;
		  ans+=count(1,1,n,1,posb);
		  if (lft==s) ans--;
		}
	  }
	  else
	  {
	    posa=(fir-a+1+n)%n+1;
		posb=(fir-b+1+n)%n+1;
		if (posa>=posb) ans=count(1,1,n,posb,posa);
		else
		{
		  int s;
		  ans+=count(1,1,n,posb,n);
		  s=rht;lft=rht=0;
		  ans+=count(1,1,n,1,posa);
		  if (lft==s) ans--;
		}
	  }
	  printf("%d\n",ans);
	}
  }
  
  return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值