【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;
}