BZOJ4293: [PA2015]Siano
好像这几天的SAM的博客都忘记更了 咕咕咕(有时间补上
言归正传 这个题主要的性质是 相对增长趋势不会发生变化 那我们根据操作的性质 可以得出 每个时刻都满足初始状态下增长趋势的相对大小关系 就是说初始值大的在接下来的时刻也一定会大因此我们可以按照这个方式去建线段树 然后维护每次查询后每个位置初始值即可 普通的线段树操作
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <stack> #include <queue> #include <cmath> #include <set> #include <map> #define mp make_pair #define pb push_back #define pii pair<int,int> #define link(x) for(edge *j=h[x];j;j=j->next) #define inc(i,l,r) for(int i=l;i<=r;i++) #define dec(i,r,l) for(int i=r;i>=l;i--) const int MAXN=5e5+10; const double eps=1e-8; #define ll long long using namespace std; //struct edge{int t;edge*next;}e[MAXN<<1],*h[MAXN],*o=e; //void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;} ll read(){ ll x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } ll flag[MAXN<<2],add[MAXN<<2],minn[MAXN<<2],sum1[MAXN<<2],sum2[MAXN<<2],a[MAXN]; //sum1 cs sum2 zeng void up(int x){ minn[x]=min(minn[x<<1],minn[x<<1|1]); sum1[x]=sum1[x<<1]+sum1[x<<1|1]; sum2[x]=sum2[x<<1]+sum2[x<<1|1]; } void built(int rt,int l,int r){ flag[rt]=-1;add[rt]=minn[rt]=sum1[rt]=0; if(l==r){sum2[rt]=a[l];return ;} int mid=(l+r)>>1; built(rt<<1,l,mid); built(rt<<1|1,mid+1,r); up(rt); } void push(int x,int l,int r){ int mid=(l+r)>>1; if(flag[x]!=-1){ add[x<<1]=add[x<<1|1]=0;flag[x<<1]=flag[x<<1|1]=flag[x]; minn[x<<1]=minn[x<<1|1]=flag[x];sum1[x<<1]=1LL*(mid-l+1)*flag[x];sum1[x<<1|1]=1LL*(r-mid)*flag[x]; flag[x]=-1; } if(add[x]!=0){ add[x<<1]+=add[x];add[x<<1|1]+=add[x];minn[x<<1]+=1LL*add[x]*a[l];minn[x<<1|1]+=1LL*add[x]*a[mid+1]; sum1[x<<1]+=sum2[x<<1]*add[x];sum1[x<<1|1]+=sum2[x<<1|1]*add[x]; add[x]=0; } } void update1(int rt,int l,int r,int ql,int qr,ll t){ if(ql<=l&&r<=qr){add[rt]+=t;minn[rt]+=1LL*t*a[l];sum1[rt]+=1LL*sum2[rt]*t;return ;} int mid=(l+r)>>1; push(rt,l,r); if(ql<=mid)update1(rt<<1,l,mid,ql,qr,t); if(qr>mid)update1(rt<<1|1,mid+1,r,ql,qr,t); up(rt); } void update2(int rt,int l,int r,int ql,int qr,ll t){ if(ql<=l&&r<=qr){add[rt]=0;flag[rt]=t;minn[rt]=t;sum1[rt]=1LL*t*(r-l+1);return ;} int mid=(l+r)>>1; push(rt,l,r); if(ql<=mid)update2(rt<<1,l,mid,ql,qr,t); if(qr>mid)update2(rt<<1|1,mid+1,r,ql,qr,t); up(rt); } ll ans1,ans2,key,Time;int id; void Find(int x,int l,int r){ if(l==r){ if(minn[x]+1LL*Time*a[l]>=key){ans1+=sum1[x];ans2+=sum2[x];id=l;} return ; } int mid=(l+r)>>1; push(x,l,r); if(minn[x<<1|1]+1LL*Time*a[mid+1]>=key){ans1+=sum1[x<<1|1];ans2+=sum2[x<<1|1];id=mid+1;Find(x<<1,l,mid);} else Find(x<<1|1,mid+1,r); up(x); } int main(){ int n,m;n=read();m=read(); for(int i=1;i<=n;i++)a[i]=read(); sort(a+1,a+n+1);built(1,1,n);ll d,b,last=0; while(m--){ d=read();b=read();Time=d-last;key=b;ans1=ans2=id=0;Find(1,1,n); if(!id){puts("0");continue;} ll ans=ans1+ans2*Time-1LL*(n-id+1)*b; printf("%lld\n",ans); if(id!=1)update1(1,1,n,1,id-1,Time); update2(1,1,n,id,n,b); last=d; } }
4293: [PA2015]Siano
Time Limit: 30 Sec Memory Limit: 256 MBSubmit: 665 Solved: 237
[Submit][Status][Discuss]
Description
农夫Byteasar买了一片n亩的土地,他要在这上面种草。
他在每一亩土地上都种植了一种独一无二的草,其中,第i亩土地的草每天会长高a[i]厘米。
Byteasar一共会进行m次收割,其中第i次收割在第d[i]天,并把所有高度大于等于b[i]的部分全部割去。Byteasar想知道,每次收割得到的草的高度总和是多少,你能帮帮他吗?
Input
第一行包含两个正整数n,m(1<=n,m<=500000),分别表示亩数和收割次数。
第二行包含n个正整数,其中第i个数为a[i](1<=a[i]<=1000000),依次表示每亩种植的草的生长能力。
接下来m行,每行包含两个正整数d[i],b[i](1<=d[i]<=10^12,0<=b[i]<=10^12),依次描述每次收割。
数据保证d[1]<d[2]<...<d[m],并且任何时刻没有任何一亩草的高度超过10^12。
Output
输出m行,每行一个整数,依次回答每次收割能得到的草的高度总和。
Sample Input
4 4
1 2 4 3
1 1
2 2
3 0
4 4
1 2 4 3
1 1
2 2
3 0
4 4
Sample Output
6
6
18
0
6
18
0
HINT
第1天,草的高度分别为1,2,4,3,收割后变为1,1,1,1。
第2天,草的高度分别为2,3,5,4,收割后变为2,2,2,2。
第3天,草的高度分别为3,4,6,5,收割后变为0,0,0,0。
第4天,草的高度分别为1,2,4,3,收割后变为1,2,4,3。