STREETTA - The Street

两种操作可以分开处理。
第一种操作:开一棵线段树,线段树上每个节点记录一个影响整个区间的等差数列。
第二种操作直接打标记就好了,复杂度O(logn)
这题真心烦。理解了一个上午,写了一个下午,搞笑了一个晚上,又调了一个上午才A。
其实理解难度还是不大的,只是没人愿意好好讲。但是写起来细节真的很多。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define sqr(x) ((x)*(x))
#define G getchar()
#define LL long long
#define pll pair<LL,LL>
#define mkp make_pair
#define X first
#define Y second
#define N 400005
#define NN 10000000
#define inf 4611686018427387904LL
#define eps 0.000001
int n;
int rt1,id1,lc1[NN],rc1[NN];LL a1[NN],d1[NN],MAX;
int rt2,id2,lc2[NN],rc2[NN];LL a2[NN],d2[NN],lza[NN],lzd[NN],SUM;
int read(){
	int x=0;char ch=G;bool flg=0;
	for(;ch<48||ch>57;ch=G)flg|=ch==45;
	for(;ch>47&&ch<58;ch=G)x=x*10+ch-48;
	return flg?-x:x;
}
int dcmp(double x){
	if(fabs(x)<eps)return 0;
	return x<0?-1:1;
}
double Ict(LL a,LL d,LL a0,LL d0){
	return (double)(a0-a)/(d-d0);
}
void Add(LL a,LL d,int l,int r,int&num,int fa){
	LL &a0=a1[num],&d0=d1[num],b0=a0+d0*(r-l),b=a+d*(r-l);
	if(a0>=a&&b0>=b)return;
	if(a>=a0&&b>=b0){a0=a;d0=d;return;}
	double tmp=Ict(a,d,a0,d0);int mid=l+r>>1;
	if(dcmp(l+tmp-mid)<=0){
		if(b>b0){
			if(!lc1[num]){a1[lc1[num]=++id1]=a;d1[lc1[num]]=d;}
			Add(a0,d0,l,mid,lc1[num],num);a0=a;d0=d;
		}
		else{
			if(!lc1[num]){a1[lc1[num]=++id1]=a0;d1[lc1[num]]=d0;}
			Add(a,d,l,mid,lc1[num],num);
		}
	}
	else{
		if(a>a0){
			if(!rc1[num]){a1[rc1[num]=++id1]=a+(mid+1-l)*d;d1[rc1[num]]=d;}
			Add(a0+(mid+1-l)*d0,d0,mid+1,r,rc1[num],num);a0=a;d0=d;
		}
		else{
			if(!rc1[num]){a1[rc1[num]=++id1]=a0+(mid+1-l)*d0;d1[rc1[num]]=d0;}
			Add(a+(mid+1-l)*d,d,mid+1,r,rc1[num],num);
		}
	}
}
void add1(int L,int R,LL a,LL d,int l,int r,int&num,int fa){
	if(L<=l&&r<=R){
		Add(a+(l-L)*d,d,l,r,num,fa);return;
	}
	int mid=l+r>>1;
	if(R>mid){
		if(!rc1[num]){a1[rc1[num]=++id1]=a1[num]+(mid+1-l)*d1[num];d1[rc1[num]]=d1[num];}
		add1(L,R,a,d,mid+1,r,rc1[num],num);
	}
	if(L<=mid){
		if(!lc1[num]){a1[lc1[num]=++id1]=a1[num];d1[lc1[num]]=d1[num];}
		add1(L,R,a,d,l,mid,lc1[num],num);
	}
}
void pd(int num,int x){
	if(lc2[num]){
		a2[lc2[num]]+=lza[num];
		lza[lc2[num]]+=lza[num];
		d2[lc2[num]]+=lzd[num];
		lzd[lc2[num]]+=lzd[num];
	}	
	if(rc2[num]){
		a2[rc2[num]]+=lza[num]+lzd[num]*(x+1>>1);
		lza[rc2[num]]+=lza[num]+lzd[num]*(x+1>>1);
		d2[rc2[num]]+=lzd[num];
		lzd[rc2[num]]+=lzd[num];
	}
	lza[num]=lzd[num]=0;
}
void add2(int L,int R,LL a,LL d,int l,int r,int &num,int fa){
	if(L<=l&&r<=R){
		a2[num]+=a+=(l-L)*d;lza[num]+=a;d2[num]+=d;lzd[num]+=d;return;
	}
	int mid=l+r>>1;pd(num,r-l+1);
	if(R>mid){
		if(!rc2[num]){a2[rc2[num]=++id2]=a2[num]+(mid+1-l)*d2[num];d2[rc2[num]]=d2[num];}
		add2(L,R,a,d,mid+1,r,rc2[num],num);
	}
	if(L<=mid){
		if(!lc2[num]){a2[lc2[num]=++id2]=a2[num];d2[lc2[num]]=d2[num];}
		add2(L,R,a,d,l,mid,lc2[num],num);
	}
}
void query1(int x,int l,int r,int num){
	if(!num)return;
	MAX=max(MAX,a1[num]+(x-l)*d1[num]);
	int mid=l+r>>1;
	if(x>mid)query1(x,mid+1,r,rc1[num]);
	else query1(x,l,mid,lc1[num]);
}
void query2(int x,int l,int r,int num){
	if(!num)return;
	if(l==r){SUM=a2[num];return;}
	pd(num,r-l+1);
	int mid=l+r>>1;
	if(x>mid)query2(x,mid+1,r,rc2[num]);
	else query2(x,l,mid,lc2[num]);
	if(SUM==-inf)SUM=a2[num]+(x-l)*d2[num];
}
int main(){
	n=read();
	int Q=read(),o,l,r,x;LL a,d;
	a1[rt1=id1=rt2=id2=1]=-inf;
	while(Q--){
		o=read();
		if(o==3){
			MAX=-inf;query1(x=read(),1,n,rt1);
			if(MAX==-inf)puts("NA");
			else{
				SUM=-inf;query2(x,1,n,rt2);
				printf("%lld\n",SUM+MAX);
			}
		}
		else{
			l=read();r=read();d=read();a=read();
			if(o==1)add1(l,r,a,d,1,n,rt1,0);
			else add2(l,r,a,d,1,n,rt2,0);
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值