Codechef March Challenge 2014——The Street
Read problems statements in Mandarin Chinese and Russian.
The String street is known as the busiest street in Codeland.
Tourists from all over the world want to visit the street once they are in Codeland.
The Chef owns N souvenir stores across the street (numbered from 1 to N).
At the beginning there is no souvenir in any store, the Chef has some plans to add some new items.
Each the Chef's plan is represented by 4 numbers: u v a b which mean an items with price b
is added to the store u, an items with price a + b is added to the store u + 1 and so on.
More formally, an item with price a * i + b is added to the store u + i for all (0 ≤ i ≤ v - u).
In additional to the cost of the item itself, the tourist must pay some conservation fees as well.
The Codeland regularly defines the new conservation fee. Each fee is represented by 4 numbers: u v a b which means
the tourist buying any item in the store u + i will be charged a fee of i * a + b for all (0 ≤ i ≤ v - u).
In the case that several conservation fees have effect on the same store, the customer needs to pay all of those fees.
At some point of time, a tourist at store i asks you what is the largest amount of money they have to spend for
a souvenir at that store (the amount of money includes the price of one of the souvenirs and all the conservation fees for that store).
Input
- The first line of the input contains two integers N and M represent the number of stores and the number of events
- Each of the next M lines represents an event of three types below in the chronological order.
- The new plan of the Chef: "1 u v a b".
- The new conservation fee: "2 u v a b".
- The query from tourist: "3 i".
Output
For each query from tourist, print in one line the corresponding answer.
If there is no item at the ith store, print out "NA" (without quotes) as the answer.
Constraints
- 1 ≤ N ≤ 109
- 1 ≤ M ≤ 3*105
- For events of type 1: 1 ≤ u ≤ v ≤ N. |a|, |b| ≤ 109
- For events of type 2: 1 ≤ u ≤ v ≤ N. |a|, |b| ≤ 104
- For events of type 3: 1 ≤ i ≤ N
Example
Input: 10 10 3 5 1 3 8 3 1 3 5 1 5 10 -8 2 3 5 3 10 2 1 10 0 1 3 6 2 5 7 2 1 3 6 Output: NA 7 7 -38 11 14
题意:
操作1:a数组的[l,r]对一个等差数列取大
操作2:给b数组的[l,r]加一个等差数列
操作3:询问 ai+bi
数据范围:10^9,所以线段树动态开点
等差数列最大值/和:一次函数
对于操作2,线段树维护a的和,b的和,当加入ax+b时,直接加上就好
对于操作1,分3种情况:
1、区间本没有等差数列,直接加上
2、区间原有的等差数列与现在的等差数列在区间内无交点,用大的直接覆盖
3、区间原有的等差数列与现在的等差数列在区间内有交点,,那么一定有一个等差数列只能对当前区间的一半区间有影响,
下传这个对一半区间有影响的等差数列,本区间保留另一个等差数列
#include<cstdio> #include<algorithm> #define INF (1LL<<62) #define N 300001 using namespace std; struct node { long long a,b,end; bool have1,cross1; bool have2,cross2; long long maxna,maxnb; }tr[N*70];; int n,m,lc[N*70],rc[N*70],tot,cnt; int op,opl,opr,root; long long ans1,ans2,A,B; void flag(int k,int who,int l,int r); long long read() { long long x=0,f=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); } while(c>='0'&&c<='9') { x=x*10+c-'0' ; c=getchar(); } return x*f; } struct TREE { void add(int &k,int l,int r,long long a,long long b) { if(!k) { k=++tot; tr[k].maxnb=-INF; } tr[k].cross1=true; if(l>=opl&&r<=opr) { tr[k].b+=b; tr[k].a+=a; tr[k].have1=true; return; } int mid=l+r>>1; if(opl<=mid) add(lc[k],l,mid,a,b); if(opr>mid) add(rc[k],mid+1,r,a,A*(mid+1-opl+1-1)+B); } double meet(long long a1,long long b1,long long a2,long long b2) { if(a1!=a2) return 1.0*(b2-b1)/(a1-a2); return 0; } void down(int &k,long long aa,long long bb,int l,int r) { flag(lc[k],aa,bb,l,l+r>>1); flag(rc[k],aa,aa*((l+r>>1)+1-l)+bb,(l+r>>1)+1,r); } void flag(int &k,long long aa,long long bb,int l,int r) { if(!k) { k=++tot; tr[k].maxnb=-INF; } tr[k].cross2=true; tr[k].have2=true; if(l==r) { tr[k].maxnb=max(tr[k].maxnb,bb); return; } if(!tr[k].maxna&&!tr[k].maxnb) { tr[k].maxna=aa; tr[k].maxnb=bb; return; } long long prea=tr[k].maxna,preb=tr[k].maxnb; long long preend=prea*(r-l+1-1)+preb; long long nowend=aa*(r-l+1-1)+bb; if(preb>=bb&&preend>=nowend) return; if(preb<=bb&&preend<=nowend) { tr[k].maxna=aa; tr[k].maxnb=bb; return; } double point=meet(tr[k].maxna,tr[k].maxnb,aa,bb); point+=l; if(point<1.0*(l+r>>1)) { if(preb>bb) { down(k,prea,preb,l,r); tr[k].maxna=aa; tr[k].maxnb=bb; } else down(k,aa,bb,l,r); } else { if(preend>nowend) { down(k,prea,preb,l,r); tr[k].maxna=aa; tr[k].maxnb=bb; } else down(k,aa,bb,l,r); } } void maxn(int &k,long long aa,long long bb,int l,int r) { if(!k) { k=++tot; tr[k].maxnb=-INF; } tr[k].cross2=true; if(l>=opl&&r<=opr) { flag(k,aa,bb,l,r); return; } if(tr[k].have2) down(k,tr[k].maxna,tr[k].maxnb,l,r); int mid=l+r>>1; if(opl<=mid) maxn(lc[k],aa,bb,l,mid); if(opr>mid) maxn(rc[k],aa,(mid+1-opl+1-1)*A+B,mid+1,r); } void query1(int k,int l,int r) { if(tr[k].have1) ans1+=tr[k].a*(opl-l+1-1)+tr[k].b; if(!tr[lc[k]].cross1&&!tr[rc[k]].cross1) return; int mid=l+r>>1; if(opl<=mid&&tr[lc[k]].cross1) query1(lc[k],l,mid); else if(opl>mid&&tr[rc[k]].cross1) query1(rc[k],mid+1,r); } void query2(int k,int l,int r) { if(tr[k].have2) ans2=max(ans2,tr[k].maxna*(opl-l+1-1)+tr[k].maxnb); if(!tr[lc[k]].cross2&&!tr[rc[k]].cross2) return; if(tr[k].have2) down(k,tr[k].maxna,tr[k].maxnb,l,r); int mid=l+r>>1; if(opl<=mid&&tr[lc[k]].cross2) query2(lc[k],l,mid); else if(opl>mid&&tr[rc[k]].cross2) query2(rc[k],mid+1,r); } }Tree; int main() { n=read(); m=read(); while(m--) { op=read(); if(op==1) { opl=read(); opr=read(); A=read(); B=read(); Tree.maxn(root,A,B,1,n); } else if(op==2) { opl=read(); opr=read(); A=read(); B=read(); Tree.add(root,1,n,A,B); } else { scanf("%d",&opl); ans2=-INF; Tree.query2(root,1,n); if(ans2==-INF) { puts("NA"); continue; } ans1=0; Tree.query1(root,1,n); printf("%lld\n",ans1+ans2); } } }
三大错误:
1、当前函数下传时,右区间首项为(mid+1-1)*a+b
mid+1时右区间第一个,再-1因为等差数列首项为 a*0+b
2、初始化:结构体内maxnb初始值为0,应该是无穷小,因为a,b可能为负数
无穷小:-2e15 会WA,直接 1LL<<62
3、down函数里下传k的等差数列后,k不能清0,因为k可能还有一个等差数列