CF::Gym题目页面传送门
给定(n)个整数坐标((x_i,y_i))。你需要支持(2)种(q)次操作:
- ( exttt0 a b c):令(x_a=b,y_a=c);
- ( exttt1 a b):一个人从((a,b))出发,每秒可以向八连通的格子走,要从对(forall iin[1,n],(x_i,y_i))完成一个来回,每次回到家要休息(2mathrm s)(共(n-1)次),求总时间。
强制在线。
(n,qinleft[0,10^5 ight])。(mathrm{TL}=1mathrm s,mathrm{ML}=64mathrm{MB})。
不难发现,对于操作( exttt1)答案就是(sumlimits_{i=1}^n2max(|x_i-a|,|y_i-b|)+2(n-1)),要求的就是前面那个(sum),即((a,b))到(n)个点的切比雪夫距离之和。
注意到,这里有绝对值,本身就需要零点分段,两个绝对值外面再套个(max),又要分类讨论,这岂不是很烦?这里需要用到一个非常经典的套路:切比雪夫转曼哈顿。
稍微说一下他们的互换吧,毕竟是第一次用。记得上hb新生班的时候,有一个同学不会abs
函数,于是就把abs(x)
写成max(x,-x)
,hm夸他很聪明。在这里,我们也可以把曼哈顿距离中的绝对值拆成(max),最终再合成绝对值:
于是不难发现,((x_1,y_1),(x_2,y_2))的曼哈顿距离等于((x_1+y_1,x_1-y_1),(x_2+y_2,x_2-y_2))的切比雪夫距离。反过来?我们设((x_1,y_1),(x_2,y_2))的切比雪夫距离等于(left(x_1',y_1' ight),left(x_2',y_2' ight))的曼哈顿距离,不难得到四元一次方程组
解得
于是,((x_1,y_1),(x_2,y_2))的切比雪夫距离等于(left(dfrac{x_1+y_1}2,dfrac{x_1-y_1}2 ight),left(dfrac{x_2+y_2}2,dfrac{x_2-y_2}2 ight))的曼哈顿距离。
回到本题,把切比雪夫距离的坐标转成曼哈顿距离的坐标(好了,下面讨论的坐标全是曼哈顿距离的坐标了)。现在(max)没有了,只剩下(+)号。不难想到将两个绝对值零点分段,分成以((a,b))为原点的四个象限,然后就可以二维数点。由于强制在线,所以只能(mathrm O!left(qlog^2v ight)),其中(v)是值域大小。虽然时间上说得过去,但空间毫无疑问会爆炸。
不过,二维数点个*啊?由于变成了(+)号,所以左边的绝对值和右边的绝对值可以分别求和,最终加到一起。这样的话,两个零点分段就是独立的,最多只有一重,也就是将整个平面分成两个半平面(而不是四个)。这样就可以一维数点了/cy
BIT?线段树?不行,因为强制在线不能离散化,基于值域建不了。动态开点线段树?抱歉,会炸空间。只剩一个选择了,就是平衡树(这里使用fhq-Treap)。维护子树内的坐标之和与实际存在的点的个数;计算答案时在平衡树内询问某个半平面内的信息,另一个半平面内的信息就用总坐标和与总点数((n))减去;一次单点增加/减少就插入一个节点,权值是对应要增加/减少的值(的相反数)。时间复杂度(mathrm O(qlog n)),空间复杂度(mathrm O(n))。
有个坑点,就是每次的lasans
输出完之后要尛一下base
,不然下次解压的时候直接乘会爆long long
。要不是hsc神仙提醒我,我就不能1A了/qq
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define mp make_pair
#define X first
#define Y second
mt19937 rng(20060617);
const int N=100000;
int n,qu,base;
int x[N+1],y[N+1];
struct fhq_treap{
int sz,root;
struct node{unsigned key;int lson,rson,sz,v1/*键值*/,v2/*坐标*/,v3/*实际点数*/,sum2/*字数内坐标和*/,sum3/*字数内实际点数*/;}nd[3*N+1];
#define key(p) nd[p].key
#define lson(p) nd[p].lson
#define rson(p) nd[p].rson
#define sz(p) nd[p].sz
#define v1(p) nd[p].v1
#define v2(p) nd[p].v2
#define v3(p) nd[p].v3
#define sum2(p) nd[p].sum2
#define sum3(p) nd[p].sum3
void init(){//初始化,一开始为空
sz=root=0;
nd[0]=node({0,0,0,0,0,0,0,0,0});
}
void sprup(int p){
sz(p)=sz(lson(p))+1+sz(rson(p));
sum2(p)=sum2(lson(p))+v2(p)+sum2(rson(p));
sum3(p)=sum3(lson(p))+v3(p)+sum3(rson(p));
}
int nwnd(int v1,int v2,int v3){return nd[++sz]=node({rng(),0,0,1,v1,v2,v3,v2,v3}),sz;}
pair<int,int> split(int x,int p=-1){~p||(p=root);
if(!x)return mp(0,p);
pair<int,int> sp;
if(x<=sz(lson(p)))return sp=split(x,lson(p)),lson(p)=sp.Y,sprup(p),mp(sp.X,p);
return sp=split(x-1-sz(lson(p)),rson(p)),rson(p)=sp.X,sprup(p),mp(p,sp.Y);
}
int mrg(int p,int q){
if(!p||!q)return p|q;
if(key(p)<key(q))return rson(p)=mrg(rson(p),q),sprup(p),p;
return lson(q)=mrg(p,lson(q)),sprup(q),q;
}
int lss(int v,int p=-1){~p||(p=root);
if(!p)return 0;
if(v1(p)<v)return sz(lson(p))+1+lss(v,rson(p));
return lss(v,lson(p));
}
void add(int x,int v2,int v3){//单点增加
pair<int,int> sp=split(lss(x));
root=mrg(mrg(sp.X,nwnd(x,v2,v3)),sp.Y);
}
pair<int,int> suM(int x){//后缀查询
// cout<<sz<<"
";
pair<int,int> sp=split(lss(x));
pair<int,int> res=mp(sum2(sp.Y),sum3(sp.Y));
return root=mrg(sp.X,sp.Y),res;
}
}trp_x,trp_y;//各维护两边绝对值之和的平衡树
signed main(){
cin>>n>>qu>>base;
int tot_x=0,tot_y=0;
for(int i=1;i<=n;i++){
scanf("%lld%lld",x+i,y+i);
int tmp=x[i];
x[i]+=y[i];y[i]=tmp-y[i];//转距离
tot_x+=x[i];tot_y+=y[i];
// cout<<x[i]<<" "<<y[i]<<"
";
}
trp_x.init();trp_y.init();//平衡树初始化
for(int i=1;i<=n;i++)trp_x.add(x[i],x[i],1),trp_y.add(y[i],y[i],1);//将初始的n个节点插入
int lasans=0;
while(qu--){
int tp,a,b,c,d,e;
scanf("%lld%lld%lld%lld%lld",&tp,&a,&b,&c,&d);
if(tp==0){
scanf("%lld",&e);
trp_x.add(x[a],-x[a],-1);trp_y.add(y[a],-y[a],-1);//单点减少
tot_x-=x[a];tot_y-=y[a];
x[a]=(b*lasans+c)%base;y[a]=(d*lasans+e)%base;
int tmp=x[a];
x[a]+=y[a];y[a]=tmp-y[a];//转距离
trp_x.add(x[a],x[a],1);trp_y.add(y[a],y[a],1);//单点增加
tot_x+=x[a];tot_y+=y[a];
}
else{
int x=(a*lasans+b)%base,y=(c*lasans+d)%base;
// cout<<x<<" "<<y<<"
";
int tmp=x;
x+=y;y=tmp-y;//转距离
// cout<<x<<" "<<y<<"
";
pair<int,int> res1=trp_x.suM(x),res2=trp_y.suM(y);//询问某个半平面内的信息
// printf("res1=(%lld,%lld) res2=(%lld,%lld)
",res1.X,res1.Y,res2.X,res2.Y);
lasans=res1.X-res1.Y*x+(n-res1.Y)*x-(tot_x-res1.X)+res2.X-res2.Y*y+(n-res2.Y)*y-(tot_y-res2.X);
lasans+=n-1<<1;//计算答案
printf("%lld
",lasans);
lasans%=base;//%%%hsctxdy
}
}
return 0;
}