The 2023 ICPC Asia EC Regionals Online Contest (I) - Problem C. Multiply Then Plus
离线询问,建立时间线段树,那么每条直线存在的时间是一个区间,对应时间线段树上$\mathcal{O}(\log n)$个节点,每个询问对应时间线段树上某个叶子到根的$\mathcal{O}(\log n)$个节点。
对于时间线段树中的某个节点,它代表的直线集合是静态的,问题转化为静态区间查询。对于静态区间查询的子问题,可以通过线段树记录区间凸壳的方式在$\mathcal{O}(n\log n)$的时间内预处理出每个线段树区间对应的凸壳,父节点的凸壳由两个子节点的凸壳线性归并得到。对于每个静态区间查询,在线段树上找到$\mathcal{O}(n\log n)$个节点,在每个节点记录的凸壳上二分查找答案。
如此一来,按时间分治、静态区间线段树以及凸壳上二分都有$\log$,总时间复杂度为$\mathcal{O}(n\log^3n)$,不能接受。如果将所有询问按$x$提前排好序的话,那么可以均摊地在$\mathcal{O}(1)$时间内找到凸壳上的答案,去掉一个$\log$。
时间复杂度$\mathcal{O}(n\log^2n)$。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int BUF=30000000; char Buf[BUF],*buf=Buf; inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;} const int OUT=10000000; char Out[OUT],*ou=Out;int Outn[30],Outcnt; inline void write(ll x){ if(!x)*ou++=48; else{ for(Outcnt=0;x;x/=10)Outn[++Outcnt]=x%10+48; while(Outcnt)*ou++=Outn[Outcnt--]; } } inline void writeln(ll x){write(x);*ou++='\n';} const int N=500005,M=500005,K=21,MAXT=M*K*2; int _,n,m,ct,i,op,x,y,z,last[N];ll ans[M]; int idx[M],tmp[M],pool[K][M],pos[N],ST,EN; struct Line{ int k,b; ll get(ll x)const{return k*x+b;} }line[N],hull[N],cur[N]; struct Tag{int x,l,r;Line v;}tag[N+M]; struct Qry{ int x,l,r; bool contain(int a,int b){return l<=a&&r>=b;} bool cross(int a,int b){return l<=b&&r>=a;} }qry[M]; int ed,g[1111111],nxt[MAXT];Tag tags[MAXT]; inline void up(ll&a,ll b){a<b?(a=b):0;} inline bool cmptag(const Tag&A,const Tag&B){ return A.x>B.x; } inline bool cmpline(const Line&A,const Line&B){ if(A.k!=B.k)return A.k<B.k; return A.b>B.b; } inline void addtag(int x,int l,int r,const Line&p){ if(l>r)return; tag[++ct].x=x; tag[ct].l=l; tag[ct].r=r; tag[ct].v=p; } void ins(int x,int a,int b,const Tag&p){ if(p.l<=a&&b<=p.r){ tags[++ed]=p; nxt[ed]=g[x]; g[x]=ed; return; } int mid=(a+b)>>1; if(p.l<=mid)ins(x<<1,a,mid,p); if(p.r>mid)ins(x<<1|1,mid+1,b,p); } inline void ext(const Line&p){ if(ST<=EN&&cur[EN].k==p.k)return; while(ST<EN&&1LL*(cur[EN-1].b-cur[EN].b)*(p.k-cur[EN].k)>=1LL*(cur[EN].b-p.b)*(cur[EN].k-cur[EN-1].k))EN--; cur[++EN]=p; } inline int makehull(int al,int ar,int bl,int br){ ST=al; EN=ST-1; while(al<=ar&&bl<=br)ext(cmpline(hull[al],hull[bl])?hull[al++]:hull[bl++]); while(al<=ar)ext(hull[al++]); while(bl<=br)ext(hull[bl++]); for(int i=ST;i<=EN;i++)hull[i]=cur[i]; return EN; } int solve(int d,int a,int b,int len){ int i,o,L=pos[a],R=pos[b]; if(a==b){ for(i=0;i<len;i++){ o=pool[d][i]; if(qry[o].l<=L&&L<=qry[o].r)up(ans[o],hull[a].get(qry[o].x)); } return a; } if(!len){ sort(hull+a,hull+b+1,cmpline); return makehull(a,b,1,0); } int mid=(a+b)>>1,ML=pos[mid],MR=pos[mid+1],cq; for(cq=i=0;i<len;i++){ o=pool[d][i]; if(!qry[o].contain(L,R)&&qry[o].cross(L,ML))pool[d+1][cq++]=o; } int el=solve(d+1,a,mid,cq); for(cq=i=0;i<len;i++){ o=pool[d][i]; if(!qry[o].contain(L,R)&&qry[o].cross(MR,R))pool[d+1][cq++]=o; } int er=solve(d+1,mid+1,b,cq); int en=makehull(a,el,mid+1,er); int st=a; for(i=0;i<len;i++){ o=pool[d][i]; if(!qry[o].contain(L,R))continue; int x=qry[o].x; while(st<en&&hull[st].get(x)<hull[st+1].get(x))st++; up(ans[o],hull[st].get(x)); } return en; } void dfs(int x,int a,int b){ if(a==b)idx[a]=a; else{ int mid=(a+b)>>1; dfs(x<<1,a,mid); dfs(x<<1|1,mid+1,b); int i=a,j=mid+1,k=a; while(i<=mid&&j<=b)tmp[k++]=qry[idx[i]].x<qry[idx[j]].x?idx[i++]:idx[j++]; while(i<=mid)tmp[k++]=idx[i++]; while(j<=b)tmp[k++]=idx[j++]; for(i=a;i<=b;i++)idx[i]=tmp[i]; } if(!g[x])return; for(int i=a;i<=b;i++)pool[0][i-a]=idx[i]; int m=0; for(int i=g[x];i;i=nxt[i]){ pos[m]=tags[i].x; hull[m]=tags[i].v; m++; } solve(0,0,m-1,b-a+1); } int main(){ fread(Buf,1,BUF,stdin); read(n);read(_); for(i=1;i<=n;i++){ read(line[i].k); read(line[i].b); last[i]=1; } while(_--){ read(op);read(x);read(y);read(z); if(op==1){ addtag(x,last[x],m,line[x]); line[x].k=y; line[x].b=z; last[x]=m+1; }else{ m++; qry[m].x=x; qry[m].l=y; qry[m].r=z; } } if(!m)return fwrite(Out,1,ou-Out,stdout),0; for(i=1;i<=n;i++)addtag(i,last[i],m,line[i]); sort(tag+1,tag+ct+1,cmptag); for(i=1;i<=ct;i++)ins(1,1,m,tag[i]); dfs(1,1,m); for(i=1;i<=m;i++)writeln(ans[i]); fwrite(Out,1,ou-Out,stdout); }