洛谷P3246 [HNOI2016]序列 [莫队]

传送门


思路

看到可离线、无修改、区间询问,相信一定可以想到莫队。

然而,莫队怎么转移是个大问题。

考虑\([l,r]\rightarrow[l,r+1]\)时答案会怎样变化?(左端点变化时同理)

\(ans+=\sum_{i=l}^r \min\{a_i,a_{i+1} ,\dots ,a_r\}\)

那么这东西如何快速统计呢?

考虑使用前缀和。

首先,显然要用单调栈预处理每个点左边最靠右的第一个比它小的数的位置\(L_i\),和ST表处理出RMQ的位置。

预处理出对于每一个\(r\)\(F(r)=\sum_{i=1}^r \min\{a_i,a_{i+1} ,\dots ,a_r\}\),方法如下:
\[ F(r)=F(L_r)+a_r\times (r-L_r) \]
上面公式的意思是:\((L_r,r]\)这一段带来的贡献是\(a_r\),其他就和\(a_r\)无关,可以用\(F(L_r)\)代替了。

然后,\([l,r-1]\rightarrow[l,r]\)时就有:
\[ \Delta ans=F(r)-F(L_{pos})-a_{pos}\times (l-1-L_{pos}) \]
其中\(pos\)表示\([l,r]\)中最小值的位置。

于是就做完了。


代码

#include<bits/stdc++.h>
clock_t t=clock();
namespace my_std{
    using namespace std;
    #define pii pair<int,int>
    #define fir first
    #define sec second
    #define MP make_pair
    #define rep(i,x,y) for (int i=(x);i<=(y);i++)
    #define drep(i,x,y) for (int i=(x);i>=(y);i--)
    #define go(x) for (int i=head[x];i;i=edge[i].nxt)
    #define templ template<typename T>
    #define sz 101100
    typedef long long ll;
    typedef double db;
    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    templ inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
    templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
    templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
    templ inline T min(T x,T y){return x<y?x:y;}
    templ inline T max(T x,T y){return x>y?x:y;}
    templ inline void read(T& t)
    {
        t=0;char f=0,ch=getchar();double d=0.1;
        while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
        while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
        if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
        t=(f?-t:t);
    }
    template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
    void file()
    {
        #ifndef ONLINE_JUDGE
        freopen("a.in","r",stdin);
        #endif
    }
    inline void chktime()
    {
        #ifndef ONLINE_JUDGE
        cout<<(clock()-t)/1000.0<<'\n';
        #endif
    }
    #ifdef mod
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
    ll inv(ll x){return ksm(x,mod-2);}
    #else
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;return ret;}
    #endif
//  inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;

int n,m;
ll a[sz];

int L[sz],R[sz];
ll Fl[sz],Fr[sz];
int st[sz][23],lg2[sz];
#define cmp(x,y) ((a[x]<a[y])?(x):(y))
inline int query(int l,int r){int len=lg2[r-l+1];return cmp(st[l][len],st[r-(1<<len)+1][len]);}
void init()
{
    rep(i,1,n) st[i][0]=i;
    rep(i,2,n) lg2[i]=lg2[i>>1]+1;
    rep(i,1,20)
        rep(j,1,n-(1<<i)+1)
            st[j][i]=cmp(st[j][i-1],st[j+(1<<(i-1))][i-1]);
    stack<int>s;
    rep(i,1,n)
    {
        while (!s.empty()&&a[s.top()]>=a[i]) s.pop();
        L[i]=(s.empty()?0:s.top());
        s.push(i);
    }
    while (!s.empty()) s.pop();
    drep(i,n,1)
    {
        while (!s.empty()&&a[s.top()]>a[i]) s.pop();
        R[i]=(s.empty()?n+1:s.top());
        s.push(i);
    }
    rep(i,1,n) Fl[i]=a[i]*ll(i-L[i])+Fl[L[i]];
    drep(i,n,1) Fr[i]=a[i]*ll(R[i]-i)+Fr[R[i]];
}
#undef cmp

inline ll queryL(int l,int r) /* [l+1,r]->[l,r] */ 
{
    int pos=query(l,r),Rpos=R[pos];
    return Fr[l]-Fr[Rpos]-a[pos]*ll(Rpos-1-r);
}
inline ll queryR(int l,int r) /* [l,r-1]->[l,r] */
{
    int pos=query(l,r),Lpos=L[pos];
    return Fl[r]-Fl[Lpos]-a[pos]*ll(l-1-Lpos);
}

int blo,pos[sz];
void Init(){blo=n/sqrt(m);rep(i,1,n) pos[i]=i/blo;}
struct hh{int l,r,id;}q[sz];
inline bool cmp(const hh &x,const hh &y){return pos[x.l]==pos[y.l]?((pos[x.l]&1)?x.r<y.r:x.r>y.r):pos[x.l]<pos[y.l];}
ll Ans[sz];

int main()
{
    file();
    read(n,m);
    rep(i,1,n) read(a[i]);
    int l,r;
    rep(i,1,m) read(l,r),q[i]=(hh){l,r,i};
    init();Init();sort(q+1,q+m+1,cmp);
    ll ans=0;
    int LL=1,RR=0;
    rep(i,1,m)
    {
        int l=q[i].l,r=q[i].r;
        while (LL>l) --LL,ans+=queryL(LL,RR);
        while (RR<r) 
            ++RR,ans+=queryR(LL,RR);
        while (LL<l) ans-=queryL(LL,RR),++LL;
        while (RR>r) ans-=queryR(LL,RR),--RR;
        Ans[q[i].id]=ans;
    }
    rep(i,1,m) printf("%lld\n",Ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/p-b-p-b/p/10398250.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值