Description
西比拉系统正在观察一个人的心理。
这个人的心理可以抽象为一条数轴,上面存在一些非负整点,被称为「意识」。
观察的方式是选取一个非负整点作为采样点,尝试捕捉这个点附近的意识。
对于一次采样,西比拉系统会吸引在区间 \([l,r]\) 内的意识,这时意识会向采样点靠近,此处的 \([l,r]\) 称为这次采样的吸引区间。
每次采样又会有一个参数,捕捉半径 \(t\),表示如果有意识可以被吸引而移动到 \([x1-t,x1+t]\) 范围内,其数据就会被捕捉。其中 \(x1\) 表示采样点的位置。
注意,当一次采样结束,所有意识都会回到原来的位置。
每个意识会有一个参数,思维强度 \(s\),表示这个意识被吸引时的可移动范围为 \([x2-s,x2+s]\),其中 \(x2\) 表示这个意识出现的位置。
初始时数轴上没有意识,此后每个时刻仅会发生一个事件。
事件有两种:
·意识事件,形如 \(T \ x2 \ s\),表示在 \(x2\) 处出现了一个思维强度为 \(s\) 的意识。
·采样事件,形如 \(G \ x1 \ l \ r \ t\),表示在 \(x1\) 处进行一次吸引区间为 \([l,r]\),捕捉半径为 \(t\),请求出会被捕捉到的意识的个数。
对于每次采样,请求出会被捕捉到的意识的个数。
Solution
容易发现,就是需要统计满足条件
\[\begin{cases} |x_1-x_2|\leq t+s \\ l\leq x_2 \leq r \end{cases} \]的点的个数。按照套路,把绝对值拆开,可以得到两种限制条件
\[\begin{cases} x_1<x_2\leq r \\ x_2-s\leq t+x_1 \end{cases} \text{or}\ \begin{cases} l\leq x_2 \leq x_1\\ x_2+s\geq x_1-t \end{cases} \]加上时间限制,跑两遍CDQ分治即可,内层用平衡树查区间数的个数。
考场代码写得有点不一样,是把询问差分了,但是是一个道理。
#include<stdio.h>
#include<algorithm>
#include<stdlib.h>
using namespace std;
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
const int N=2e5+7;
struct Que{
int pos,x,s; bool op; int P;
Que(int pos_=-1,int x_=0,int s_=0,bool op_=0,int P_=0):
pos(pos_),x(x_),s(s_),op(op_),P(P_){}
}a[N];
struct Node{
int sz,key,ls,rs,val;
}t[N];
int top=0,rev[N],tp=0,ans[N];
inline bool Cmp1(const Que &X,const Que &Y){
int x=X.x,y=Y.x;
if(~X.pos) x=min(x,X.pos);
if(~Y.pos) y=min(y,Y.pos);
return x<y;
}
inline bool Cmp2(const Que &X,const Que &Y){
int x=(~X.pos)? X.x+X.s:X.x-X.s;
int y=(~Y.pos)? Y.x+Y.s:Y.x-Y.s;
return x<y;
}
void Del(int id){
if(t[id].ls) Del(t[id].ls);
if(t[id].rs) Del(t[id].rs);
rev[++tp]=id;
}
inline int New(int x){
static int cnt=0; int p;
if(tp) p=rev[tp--];
else p=++cnt;
t[p]=(Node){1,rand(),0,0,x};
return p;
}
inline void update(int id){
t[id].sz=t[t[id].ls].sz+t[t[id].rs].sz+1;
}
int merge(int x,int y){
if(!x||!y) return x+y;
if(t[x].key<t[y].key){
t[x].rs=merge(t[x].rs,y);
return update(x),x;
}else{
t[y].ls=merge(x,t[y].ls);
return update(y),y;
}
}
void split(int id,int k,int &x,int &y){
if(!id){x=y=0;return;}
if(t[id].val<=k)
x=id,split(t[id].rs,k,t[id].rs,y);
else y=id,split(t[id].ls,k,x,t[id].ls);
update(id);
}
void CDQ(int lf,int rf){
if(lf==rf) return ;
int mid=(lf+rf)>>1;
CDQ(lf,mid),CDQ(mid+1,rf);
sort(a+lf,a+mid+1,Cmp1),sort(a+mid+1,a+rf+1,Cmp1);
int rt=0,x,y,z;
for(int i=mid+1,j=lf;i<=rf;i++){
if(a[i].pos==-1) continue;
const int rg=min(a[i].pos,a[i].x);
while(j<=mid&&((~a[j].pos)||(a[j].x<=rg))){
if(~a[j].pos){j++;continue;}
split(rt,a[j].x+a[j].s,x,y);
rt=merge(merge(x,New(a[j].x+a[j].s)),y);
j++;
}
split(rt,a[i].x-a[i].s-1,x,y);
ans[a[i].P]+=(a[i].op? 1:-1)*t[y].sz;
rt=merge(x,y);
}
if(rt) Del(rt); rt=0;
sort(a+lf,a+mid+1,Cmp2),sort(a+mid+1,a+rf+1,Cmp2);
for(int i=mid+1,j=lf;i<=rf;i++){
if(a[i].pos==-1) continue;
if(a[i].x>=a[i].pos) continue;
const int rg=a[i].x+a[i].s;
while(j<=mid&&((~a[j].pos)||(a[j].x-a[j].s<=rg))){
if(~a[j].pos){j++;continue;}
split(rt,a[j].x,x,y);
rt=merge(merge(x,New(a[j].x)),y);
j++;
}
split(rt,a[i].pos,y,z);
split(y,a[i].x,x,y);
ans[a[i].P]+=(a[i].op? 1:-1)*t[y].sz;
rt=merge(merge(x,y),z);
}
if(rt) Del(rt);
}
int main(){
freopen("mind.in","r",stdin);
freopen("mind.out","w",stdout);
srand(114514);
int n=read(),q=0; char op[5];
for(int i=1;i<=n;i++){
scanf("%s",op);
if(op[0]=='T'){
int x=read()+1,s=read();
a[++top]=Que(-1,x,s);
}else{
q++;
int x=read()+1,l=read()+1,r=read()+1,s=read();
a[++top]=Que(l-1,x,s,0,q),a[++top]=Que(r,x,s,1,q);
}
}
CDQ(1,top);
for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
}