BZOJ4540: [Hnoi2016]序列 (线段树)

传送门

题解:
这道题的线段树做法好神啊。。

考虑直接从 1 1 扫描到 n n ,不断更新 1~i 1 ~ i i i 的最小值。

那么显然我们要对线段树支持区间覆盖, 区间求历史和。

然后我们可以构造矩阵来完成,对于每个节点构造

svallen ( s v a l l e n )

即可完成区间覆盖。(主要是多维护了一个 len l e n

查询直接求和即可。

#include 
typedef long long LL;
using namespace std;
inline int rd() {
    char ch=getchar(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
    return i*f;
}
const int N=1e5+50;
int n,q,a[N],p[N],st[N],top;
vector < pair<int,int> >qry[N];
struct Tag {
    LL a,b,c,d;
    Tag(LL a=0,LL b=0,LL c=1,LL d=0):a(a),b(b),c(c),d(d){}
    friend inline Tag operator *(const Tag &a,const Tag &b) {
        return Tag(b.a+a.a*b.c, b.b+a.a*b.d+a.b, a.c*b.c, a.c*b.d+a.d);
    } 
}tag[N*4];
LL s1[N*4],s2[N*4],ans[N];
inline void modify(int k,int l,int r,Tag t) {
    s1[k]=s1[k]+t.a*s2[k]+t.b*(r-l+1);
    s2[k]=t.c*s2[k]+t.d*(r-l+1);
    tag[k]=t*tag[k];
}
inline void pushdown(int k,int l,int r) {
    int mid=(l+r)>>1;
    modify(k<<1,l,mid,tag[k]);
    modify(k<<1|1,mid+1,r,tag[k]);
    tag[k]=Tag(0,0,1,0);
}
inline void upt(int k) {
    s1[k]=s1[k<<1]+s1[k<<1|1];
    s2[k]=s2[k<<1]+s2[k<<1|1];
}
inline void modify(int k,int l,int r,int L,int R,Tag t) {
    if(L<=l&&r<=R) {modify(k,l,r,t); return;}
    pushdown(k,l,r);
    int mid=(l+r)>>1;
    if(R<=mid) modify(k<<1,l,mid,L,R,t);
    else if(L>mid) modify(k<<1|1,mid+1,r,L,R,t);
    else modify(k<<1,l,mid,L,R,t),modify(k<<1|1,mid+1,r,L,R,t);
    upt(k);
}
inline LL query(int k,int l,int r,int L,int R) {
    if(L<=l&&r<=R) {return s1[k];}
    pushdown(k,l,r);
    int mid=(l+r)>>1;
    if(R<=mid) return query(k<<1,l,mid,L,R);
    else if(L>mid) return query(k<<1|1,mid+1,r,L,R);
    else return query(k<<1,l,mid,L,R)+query(k<<1|1,mid+1,r,L,R);
}
int main() {
    n=rd(), q=rd();
    for(int i=1;i<=n;i++) a[i]=rd();
    for(int i=1;i<=q;i++) {
        int x=rd(), y=rd();
        qry[y].push_back(make_pair(x,i));
    }
    for(int i=1;i<=n;i++) {
        while(top&&st[top]>=a[i]) --top;
        modify(1,1,n,p[top]+1,i,Tag(0,0,0,a[i]));
        st[++top]=a[i]; p[top]=i;
        modify(1,1,n,1,i,Tag(1,0,1,0));
        for(int j=qry[i].size()-1;~j;j--) {
            int v=qry[i][j].first;
            ans[qry[i][j].second]=query(1,1,n,v,i);
        }
    }
    for(int i=1;i<=q;i++) printf("%lld\n",ans[i]);
}

你可能感兴趣的:(DP及DP优化,线段树)