题目链接
题目大意
给了一个序列 (A_1,A_2,...,A_N),求出满足 (1leq X_ileq A_i) 且相邻元素不同的序列 (X) 的数量,答案模 (998244353) 。
(2leq Nleq 5 imes 10^5),(1leq A_ileq 10^9)
思路
考虑容斥,设 (f_i) 为前 (i) 位的答案,有转移式:
[f_i=sum_{j=0}^{i-1}f_jcdot min_{j<kleq 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;
}