线段树维护一次函数
评测地址:https://www.codechef.com/problems/STREETTA
操作1:[l,r] 函数A对ax+b取大
操作2:[l,r] 函数B增加一次函数ax+b
操作3:询问 函数A在x位置的最大值+函数B在x位置的值,没有输出NA
r<=1e9
|a|,|b|<=1e9
操作数<=3e5
每次操作大约用log个节点,标记下传还要带log,所以线段树动态开节点
线段树标记永久化,询问从根一直走到它
函数增加直接加
函数取大:
1、原区间无函数,直接覆盖
2、无交点,用截距大的覆盖
3、交点在区间左端点,用斜率大的覆盖
4、交点在区间右端点,用斜率小的覆盖
5、交点在左子区间,下传截距大的,留下截距小的
6、交点在右子区间,下传截距小的,留下截距大的
(5、6 是在区间内部有交点,一定有一个函数只会影响一个子区间,下传这个函数)
下传函数时,
往左子区间传,斜率、截距都不改变
往右子区间传,斜率不变,截距改变
注意:代码中求函数交点,得到的是交点位置是将区间左端点看做0
#include<cmath> #include<cstdio> #include<iostream> #include<algorithm> using namespace std; typedef long long LL; #define N 300001 const double eps=1e-7; struct node { LL mxa,mxb; LL sma,smb; int lc,rc; bool HaveMx; }tr[N*70]; int tot; bool ok; LL AnsMx,AnsSm; void read(int &x) { x=0; int f=1; char c=getchar(); while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); } while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } x*=f; } void query(int k,int l,int r,int pos) { if(tr[k].HaveMx) { ok=true; AnsMx=max(AnsMx,(pos-l)*tr[k].mxa+tr[k].mxb); } AnsSm+=(pos-l)*tr[k].sma+tr[k].smb; if(l==r) return; int mid=l+r>>1; if(pos<=mid) { if(tr[k].lc) query(tr[k].lc,l,mid,pos); } else { if(tr[k].rc) query(tr[k].rc,mid+1,r,pos); } } void add(int &k,int l,int r,int opl,int opr,LL a,LL b) { if(!k) k=++tot; if(l==opl && r==opr) { tr[k].sma+=a; tr[k].smb+=b; return; } int mid=l+r>>1; if(opr<=mid) add(tr[k].lc,l,mid,opl,opr,a,b); else if(opl>mid) add(tr[k].rc,mid+1,r,opl,opr,a,b); else { add(tr[k].lc,l,mid,opl,mid,a,b); add(tr[k].rc,mid+1,r,mid+1,opr,a,a*(mid+1-opl)+b); } } double getpoint(LL a1,LL b1,LL a2,LL b2) { return 1.0*(b2-b1)/(a1-a2); } bool dcmp(double x,double y) { return fabs(x-y)<eps; } void down(int &k,int l,int r,LL a,LL b) { if(!k) k=++tot; if(l==r) { if(!tr[k].HaveMx) { tr[k].HaveMx=true; tr[k].mxa=a; tr[k].mxb=b; } else { if(b>tr[k].mxb) { tr[k].mxa=a; tr[k].mxb=b; } } return; } if(!tr[k].HaveMx) { tr[k].HaveMx=true; tr[k].mxa=a; tr[k].mxb=b; } else { if(a==tr[k].mxa) { if(b>tr[k].mxb) tr[k].mxb=b; return; } double foot=getpoint(a,b,tr[k].mxa,tr[k].mxb); if(foot<0 || foot>r-l+1) { if(b>tr[k].mxb) { tr[k].mxa=a; tr[k].mxb=b; } } else if(dcmp(foot,0)) { if(a>tr[k].mxa) { tr[k].mxa=a; tr[k].mxb=b; } } else if(dcmp(foot,r-l+1)) { if(a<tr[k].mxa) { tr[k].mxa=a; tr[k].mxb=b; } } else { int mid=l+r>>1; if(l+foot<=mid) { if(b>tr[k].mxb) down(tr[k].lc,l,mid,a,b); else { down(tr[k].lc,l,mid,tr[k].mxa,tr[k].mxb); tr[k].mxa=a; tr[k].mxb=b; } } else { if(b<tr[k].mxb) down(tr[k].rc,mid+1,r,a,a*(mid+1-l)+b); else { down(tr[k].rc,mid+1,r,tr[k].mxa,tr[k].mxa*(mid+1-l)+tr[k].mxb); tr[k].mxa=a; tr[k].mxb=b; } } } } } void getmax(int &k,int l,int r,int opl,int opr,LL a,LL b) { if(!k) k=++tot; if(l==opl && r==opr) { down(k,l,r,a,b); return; } int mid=l+r>>1; if(opr<=mid) getmax(tr[k].lc,l,mid,opl,opr,a,b); else if(opl>mid) getmax(tr[k].rc,mid+1,r,opl,opr,a,b); else { getmax(tr[k].lc,l,mid,opl,mid,a,b); getmax(tr[k].rc,mid+1,r,mid+1,opr,a,a*(mid+1-opl)+b); } } int main() { freopen("data.in","r",stdin); freopen("my.out","w",stdout); int n,m; read(n); read(m); int ty,l,r,a,b; int root=0; while(m--) { read(ty); if(ty==1) { read(l); read(r); read(a); read(b); getmax(root,1,n,l,r,a,b); } else if(ty==2) { read(l); read(r); read(a); read(b); add(root,1,n,l,r,a,b); } else { read(l); ok=false; AnsMx=-1LL<<62; AnsSm=0; query(root,1,n,l); if(!ok) puts("NA"); else cout<<AnsMx+AnsSm<<'\n'; } } }