CF453E Little Pony and Lord Tirek(线段树+二分)

考虑如果一个区间在同一时刻被清过零,怎么做。
我们可以把这个区间中的点按生成满能量的时间从小到大排序,然后用生长了的时间去二分。一定是前面一段都是满能量,可以前缀和算,后面一段也可以r的后缀和×生长时间算。
我们线段树维护一下这个东西。
如果区间内有未清过零的点打标记-2.
如果一个区间被统一清过零,打标记清零时间>=0
如果清零时间不统一打标记-1
如果一个区间被完全覆盖并且打标记时间统一就可以直接做,否则暴力递归下去。
可以证明这样的复杂度是 O(nlog2n) O ( n l o g 2 n )

#include 
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 100010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,m,T;
struct Icefox{
    int s,m,r,t;
}a[N];
inline bool cmp(int x,int y){return a[x].tstruct node{
    vectorM,R;vector<int>b;
    int tag;//-2 --没清零过 -1 --清零时间不一  >=0 最后清零时刻为tag
    inline void init(int l,int r){
        for(int i=l;i<=r;++i) b.push_back(i);sort(b.begin(),b.end(),cmp);
        M.push_back(a[b[0]].m);R.push_back(a[b[0]].r);
        for(int i=l+1;i<=r;++i) M.push_back(M.back()+a[b[i-l]].m),R.push_back(R.back()+a[b[i-l]].r);
    }inline ll gao(){
        a[n+1].t=T-tag;
        int id=lower_bound(b.begin(),b.end(),n+1,cmp)-b.begin();
        return id?M[id-1]+(R.back()-R[id-1])*(T-tag):R.back()*(T-tag);
    }
}tr[N<<2];
inline void build(int p,int l,int r){
    tr[p].tag=-2;tr[p].init(l,r);if(l==r) return;int mid=l+r>>1;
    build(p<<1,l,mid);build(p<<1|1,mid+1,r);
}
inline void pushdown(int p){
    if(tr[p].tag>=0) tr[p<<1].tag=tr[p].tag,tr[p<<1|1].tag=tr[p].tag;
}
inline void pushup(int p){
    if(tr[p<<1].tag==-2||tr[p<<1|1].tag==-2){tr[p].tag=-2;return;}
    if(tr[p<<1].tag>=0&&tr[p<<1].tag==tr[p<<1|1].tag){tr[p].tag=tr[p<<1].tag;return;}
    tr[p].tag=-1;return;
}
inline ll ask(int p,int l,int r,int x,int y){
    if(l==r&&tr[p].tag==-2){ll res=min(a[l].s+(ll)a[l].r*T,(ll)a[l].m);tr[p].tag=T;return res;}
    if(x<=l&&r<=y&&tr[p].tag>=0){
        ll res=tr[p].gao();tr[p].tag=T;return res;
    }int mid=l+r>>1;ll res=0;pushdown(p);
    if(x<=mid) res+=ask(p<<1,l,mid,x,y);
    if(y>mid) res+=ask(p<<1|1,mid+1,r,x,y);
    pushup(p);return res;
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();
    for(int i=1;i<=n;++i) a[i].s=read(),a[i].m=read(),a[i].r=read(),a[i].t=a[i].r?a[i].m/a[i].r:inf;
    build(1,1,n);int m=read();
    while(m--){
        T=read();int l=read(),r=read();
        printf("%lld\n",ask(1,1,n,l,r));
    }return 0;
}

你可能感兴趣的:(codeforces,线段树,二分)