AtCoder ARC115 E - LEQ and NEQ

题目链接

E - LEQ and NEQ

题目大意

给了一个序列 \(A_1,A_2,...,A_N\),求出满足 \(1\leq X_i\leq A_i\) 且相邻元素不同的序列 \(X\) 的数量,答案模 \(998244353\)

\(2\leq N\leq 5\times 10^5\)\(1\leq A_i\leq 10^9\)

思路

考虑容斥,设 \(f_i\) 为前 \(i\) 位的答案,有转移式:

\[f_i=\sum_{j=0}^{i-1}f_j\cdot \min_{j<k\leq i}\{A_k\}\cdot (-1)^{i-j-1} \]

注意到每次多一个 \(A_i\) 是会改变一个区间的转移系数的,把 \((-1)^i\) 扔出来后,用线段树维护所有地方的权值和(就是求和式的值),用栈记录 \(A_i\) 影响了哪些位置的权值,每次把当前的 \(A_i\) 插入进去,更新线段树即可。

时间复杂度 \(O(Nlog_2N)\)

Code

#include<iostream>
#include<stack>
#include<cstdio>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,b,a) for(int i=(b);i>=(a);i--)
#define N 500500
#define M 2000021
#define mod 998244353
#define ll long long
using namespace std;
int a[N],n;
struct SegmentTree{
    ll w,val,lazy;
}t[M];
stack<int> mn;
ll dp[N];
void pushdown(int x){
    if(t[x].lazy){
        t[x*2].w=(t[x*2].val*t[x].lazy)%mod;
        t[x*2+1].w=(t[x*2+1].val*t[x].lazy)%mod;
        t[x*2].lazy=t[x].lazy,t[x*2+1].lazy=t[x].lazy;
    }
}
void update(int x,int l,int r,int a,int b,ll k){
    if(l>=a&&r<=b){
        t[x].lazy=k,t[x].w=(t[x].val*k)%mod;
        return;
    }
    int mid=(l+r)>>1;
    if(mid>=a)update(x*2,l,mid,a,b,k);
    if(mid<b)update(x*2+1,mid+1,r,a,b,k);
    t[x].w=(t[x*2].w+t[x*2+1].w)%mod;
    t[x].val=(t[x*2].val+t[x*2+1].val)%mod;
}
void insert(int x,int l,int r,int loc,ll k){
    if(l==r){
        t[x].val=k,t[x].w=(t[x].val*t[x].lazy)%mod;
        return;
    }
    pushdown(x);
    int mid=(l+r)>>1;
    if(mid>=loc)insert(x*2,l,mid,loc,k);
    else insert(x*2+1,mid+1,r,loc,k);
    t[x].w=(t[x*2].w+t[x*2+1].w)%mod;
    t[x].val=(t[x*2].val+t[x*2+1].val)%mod;
}
int main(){
    ios::sync_with_stdio(false);
    cin>>n;
    rep(i,1,n)cin>>a[i];
    mn.push(0);
    dp[0]=1;
    insert(1,0,n,0,mod-dp[0]);
    rep(i,1,n){
        while(a[mn.top()]>a[i])mn.pop();
        update(1,0,n,mn.top(),i-1,a[i]);
        mn.push(i);
        dp[i]=(i&1)?mod-t[1].w:t[1].w;
        insert(1,0,n,i,(i&1)?dp[i]:mod-dp[i]);
    }
    cout<<dp[n]<<endl;
    return 0;
}

思路2

然而这道题是可以做到线性的,题目这个一个点可以控制一段区间的性质,可以考虑用笛卡尔树维护,和前面一样建一个栈,不过栈中要储存 \(A_i\) 下标和其控制的权值和两样东西,然后直接根据奇偶性计算就可以了,时间复杂度 \(O(N)\)

讲起来有点玄,看代码吧。

Code

#include<iostream>
#include<stack>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,b,a) for(int i=(b);i>=(a);i--)
#define N 500500
#define ll long long
#define mod 998244353
#define fr first
#define sc second
using namespace std;
int a[N],n;
int main(){
    ios::sync_with_stdio(false);
    cin>>n;
    rep(i,1,n)cin>>a[i];
    ll val=0,sum=0;
    stack<pair<ll,int> > s;
    rep(i,1,n){
        val=sum+(i==1);
        (sum+=mod-val*a[i]%mod)%=mod;
        while(!s.empty()&&s.top().second>a[i]){
            pair<ll,int> cur=s.top();
            s.pop();
            (sum+=cur.fr*(cur.sc-a[i]))%=mod;
            (val+=cur.fr)%=mod;
        }
        s.push({val,a[i]});
    }
    cout<<(n%2?mod-sum:sum)<<endl;
    return 0;
}
posted @ 2021-04-01 22:21  Neal_lee  阅读(568)  评论(0编辑  收藏  举报