题目:http://codeforces.com/contest/331/problem/B2
题意:
给定一个数组a1,a2,···,an,有两类操作:1.对于i1<i2<···<ik,有ai1=x,ai2=x+1,···,aik-1=y-1,aik=y,因为可能[x,y]不满足条件,可以把[x,y]分割成[x,p1], [p1+1,p2], ..., [pm+1,y] (x<p1<p2<...<pm<y),问需要把[x,y]分成多少组才能满足条件 2.把数组中给定的两个数交换位置
思路:
由于n范围比较大,显然要用到线段树或树状数组,对比序列求和的操作,先初始化(1,n),依次判断i在数组a中的位置是否小于i-1,如果是则让c[i]++,并更新到i的父节点(c[i]表示(1,i)需要分的组数),这样最终查询的结果就是c[y]-c[x]+1,难点是任意两个数交换位置后的更新操作(需要更新两次),具体见代码。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 300005;
int c[N];
int a[N],p[N];
int lowbit(int x)
{
return x&(-x);
}
int getsum(int x)
{
int sum=0;
while(x)
{
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
void up(int x,int num)
{
while(x<N)
{
c[x]+=num;
x+=lowbit(x);
}
}
int main()
{
int n,i;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
p[a[i]]=i;
}
for(i=2;i<=n;i++)
{
if(p[i]<p[i-1]) up(i,1);
}
int m;
scanf("%d",&m);
while(m--)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(x==1)
printf("%d\n",getsum(z)-getsum(y)+1);
else
{
int yy=a[y],zz=a[z];
if(p[yy]<p[yy-1]) up(yy,-1);
if(yy<n&&p[yy]>p[yy+1]) up(yy+1,-1);
if(zz-1!=yy&&p[zz]<p[zz-1]) up(zz,-1);
if(zz+1!=yy&&zz<n&&p[zz]>p[zz+1]) up(zz+1,-1);
p[yy]=z;
p[zz]=y;
a[y]=zz;
a[z]=yy;
if(p[yy]<p[yy-1]) up(yy,1);
if(yy<n&&p[yy]>p[yy+1]) up(yy+1,1);
if(zz-1!=yy&&p[zz]<p[zz-1]) up(zz,1);
if(zz+1!=yy&&zz<n&&p[zz]>p[zz+1]) up(zz+1,1);
}
}
return 0;
}