[CRCI2008-2009] CVJETICI
观察图片及样例一:

注:下文中的被占领,指的是在这一个区间内,才有交叉开花的可能。
第一张小图发现 $2 \sim 3$ 被占领。 第二张小图发现 $4 \sim 6$ 被占领,并伸入 $2 \sim 3$ 内,开花。第三张小图发现 $2 \sim 5$ 被占领,并伸入 $4 \sim 6$ 内,开花,其中 $2$ 被占领了两次,可能会开两次花,$6$ 已经开花,将来不在开花。第四张小图发现 $3 \sim 5$ 被占领,并伸入被两次占领的地方内,开两次花。
丑图献上:

这里有个问题,我们怎么判断每个点被占领了几次?,其实很简单,我们可以用线段树来处理一个区间内每个点都被占领了一次,相当于每个点加一。然后单点查询,判断被占领了几次。由于看过花的地方不能在开花,我们要减去之前有过的数量,也很简单,用数组存贮之前这个点上开过多少花就行了。
树状数组思路相同,记得用上差分。并在起点处加一,终点减一,用前缀和代表这一段的数量。
线段树:
#include<bits/stdc++.h>
using namespace std;
#define p2 (p<<1) //左右儿子
#define p3 (p<<1|1)
const int M=100005;
int n,tr[M<<2],had[M];//had是标记数组
void upd(int l,int r,int x,int y,int p){
if(l==x&&r==y){
tr[p]++;
return;
}
int mid=(l+r)/2;
tr[p2]+=tr[p];
tr[p3]+=tr[p];
tr[p]=0;
if(y<=mid) upd(l,mid,x,y,p2);
else if(x>=mid+1) upd(mid+1,r,x,y,p3);
else upd(l,mid,x,mid,p2),upd(mid+1,r,mid+1,y,p3);
}
int query(int l,int r,int x,int p){
if(l==r) return tr[p];
int mid=(l+r)/2,ret=0;
tr[p2]+=tr[p];
tr[p3]+=tr[p];
tr[p]=0;
if(x<=mid) ret=query(l,mid,x,p2);
else ret=query(mid+1,r,x,p3);
return ret;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int a,b;
scanf("%d%d",&a,&b);
if(a>b) swap(a,b);
int r1=query(1,100000,a,1);
int r2=query(1,100000,b,1);//进行查询
//查询完后,可能可以开花的数量数r1+r2,当时两朵花不能在一点,所以减去开过的花,也就是had
printf("%d\n",r1-had[a]+r2-had[b]);
//更新a点上目前有的数量r1
had[a]=r1;
had[b]=r2;
//a+1到b-1这个区间,去增加一
//为了防止没有区间,如a=1,b=2的情况要判断一下
if(a+1<=b-1) upd(1,100000,a+1,b-1,1);
}
return 0;
}
树状数组:
#include<bits/stdc++.h>
using namespace std;
const int M=100005;
int n,C[M+5],had[M+5];
void add(int x,int y){//在x的地方加上y
for(int i=x;i<=M;i+=i&-i) C[i]+=y;
}
int sum(int x){
int ret=0;
for(int i=x;i;i-=i&-i) ret+=C[i];
return ret;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int a,b;
scanf("%d%d",&a,&b);
if(a>b) swap(a,b);
int r1=sum(a);
int r2=sum(b);//进行查询
//查询完后,可能可以开花的数量数r1+r2,当时两朵花不能在一点,所以减去开过的花,也就是had
printf("%d\n",r1-had[a]+r2-had[b]);
//更新a点上目前有的数量r1
had[a]=r1;
had[b]=r2;
//a+1位置上加1,b-1位置上-1
//为了防止没有区间,如a=1,b=2的情况要判断一下
if(a+1<=b-1) add(a+1,1),add(b,-1);
}
return 0;
}
官方代码(思路差不多的,他将其分成了 $256$ 块)
#include <cstdio>
int N;
int A[100001];
int B[1000];
int main( void ) {
scanf( "%d", &N );
for( int i = 0; i < N; ++i ) {
int L, R;
scanf( "%d%d", &L, &R );
printf( "%d\n", A[L]+B[L>>8] + A[R]+B[R>>8] );
A[L] = -B[L>>8];
A[R] = -B[R>>8];
int x = L+1;
for( ; x < R && (x&0xFF); ++x ) ++A[x];
for( ; x+256 <= R; x += 256 ) ++B[x>>8];
for( ; x < R; ++x ) ++A[x];
}
return 0;
}

浙公网安备 33010602011771号