考虑将序列分成$sqrt{nlog n}$块,每块维护下凸壳,修改时在相应块打上需要修改的标记。
查询时,对于两端零散部分暴力查询。
对于中间的块,如果有修改标记,则暴力重构。
然后在凸壳上查询时不断把小于$T$的左端点踢出,那么最后如果凸壳上还有点,那么左端点一定$geq T$。
时间复杂度$O(msqrt{nlog n})$。
#include<cstdio> #include<algorithm> #define N 100010 using namespace std; typedef long long ll; const ll inf=1LL<<60; int n,m,lim,i,op,T,x,y,z,id[N],st[N],en[N],L[N],R[N],need[N],b[N],q[N];ll ans; struct P{int k;ll b;P(){}P(int _k,ll _b){k=_k,b=_b;}}a[N]; inline bool cmp(int x,int y){return a[x].k==a[y].k?a[x].b>a[y].b:a[x].k<a[y].k;} inline void read(int&a){ char c;bool f=0;a=0; while(!((((c=getchar())>='0')&&(c<='9'))||(c=='-'))); if(c!='-')a=c-'0';else f=1; while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0'; if(f)a=-a; } inline double pos(int x,int y){return 1.0*(a[x].b-a[y].b)/(a[y].k-a[x].k);} inline void build(int x){ int cnt=0,t=st[x],i; for(i=t;i<=en[x];i++)if(a[i].b>-inf||i==t)q[++cnt]=i; sort(q+1,q+cnt+1,cmp); for(b[t]=q[1],i=2;i<=cnt;i++)if(a[q[i]].k!=a[q[i-1]].k){ while(t>st[x]&&pos(q[i],b[t])<pos(b[t],b[t-1]))t--; b[++t]=q[i]; } L[x]=st[x],R[x]=t; } inline void ask(int x){ while(L[x]<R[x]&&(double)T>pos(b[L[x]],b[L[x]+1]))L[x]++; if(L[x]<=R[x])ans=max(ans,1LL*a[b[L[x]]].k*T+a[b[L[x]]].b); } inline void getans(int x,int y){ if(id[x]==id[y]){ for(;x<=y;x++)ans=max(ans,1LL*a[x].k*T+a[x].b); return; } for(i=en[id[x]];i>=x;i--)ans=max(ans,1LL*a[i].k*T+a[i].b); for(i=st[id[y]];i<=y;i++)ans=max(ans,1LL*a[i].k*T+a[i].b); for(i=id[x]+1;i<id[y];i++){ if(need[i])build(i),need[i]=0; ask(i); } } int main(){ read(n),read(m); for(i=1;1<<i<n;i++); while(lim*lim*i<n)lim++; for(i=1;i<=n;i++)en[id[i]=i/lim]=i; for(i=n;i;i--)st[id[i]]=i; for(i=1;i<=n;i++)a[i]=P(0,-inf); for(i=id[1];i<=id[n];i++)L[i]=1; while(m--){ read(op),read(T),read(x),read(y); if(op==1){ read(z); a[x]=P(y,-1LL*y*T+z); need[id[x]]=1; }else{ if(x>y)swap(x,y); ans=-inf; getans(x,y); if(ans>-inf)printf("%lld ",ans);else puts("nema"); } } return 0; }