2018.07.31cogs2964. 数列操作η(线段树)

传送门
线段树基本操作。
给出一个排列b,有一个初始值都为0的数组a,维护区间加1,区间统计区间∑ (ai/bi) ( a i / b i )
我们维护当前所有的 ai a i % bi b i bi b i 差量的最小值,这样就把第一个操作变成了区间减法。
如果当前区间的差量最小值是大于 1 1 的,说明在这次减法过后整个区间对答案不会有新的贡献,直接打上标记。
否则我们递归到叶子结点,重新赋值差量并统计对答案的贡献,整个东西开个树状数组记录就好。
代码:

#include
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
#define N 100005
using namespace std;
int n,m,b[N],bit[N],ll,rr;
struct Node{int l,r,mn,add;}T[N<<2];
inline int min(int a,int b){return ainline void pushup(int p){T[p].mn=min(T[lc].mn,T[rc].mn);}
inline void pushnow(int p,int v){T[p].mn-=v,T[p].add+=v;}
inline void pushdown(int p){pushnow(lc,T[p].add),pushnow(rc,T[p].add),T[p].add=0;}
inline int lowbit(int x){return x&-x;}
inline void modify(int p){for(int i=p;i<=n;i+=lowbit(i))++bit[i];}
inline void build(int p,int l,int r){
    T[p].l=l,T[p].r=r,T[p].add=0;
    if(l==r){scanf("%d",&b[l]),T[p].mn=b[l];return;}
    build(lc,l,mid),build(rc,mid+1,r),pushup(p);
}
inline void update(int p,int ql,int qr){
    if(ql>T[p].r||qrreturn;
    if(ql<=T[p].l&&T[p].r<=qr&&T[p].mn>1)return pushnow(p,1);
    if(T[p].l==T[p].r){T[p].mn=b[T[p].l];return modify(T[p].l);}
    pushdown(p);
    if(qr<=mid)update(lc,ql,qr);
    else if(ql>mid)update(rc,ql,qr);
    else update(lc,ql,mid),update(rc,mid+1,qr);
    pushup(p);
}
inline int query(int p){int ans=0;for(int i=p;i;i-=lowbit(i))ans+=bit[i];return ans;}
int main(){
    freopen("eta.in","r",stdin),freopen("eta.out","w",stdout),scanf("%d%d",&n,&m),build(1,1,n);
    while(m--){
        char s[10];
        scanf("%s%d%d",s,&ll,&rr);
        if(s[0]=='a')update(1,ll,rr);
        else printf("%d\n",query(rr)-query(ll-1));
    }
    return 0;
}

你可能感兴趣的:(#,线段树)