[题解] CF2045E Narrower Passageway
Luzhuoyuan · · 题解
一个简单一点的做法。
首先容易发现求的就是所有方案的总权值除以方案数
我们发现一个区间权值的计算方式是:若两行分别的最大值相等则为
设两行分别为序列
考虑统计每个数对答案的贡献。
先思考
-
\forall j\in[L_i,i),a_j\le a_i -
\forall j\in(i,R_i],a_j<a_i
上下符号不同是为了防止算重。然后它的贡献就是
考虑这个方案数如何计算。要想构成一个极长的无雾区间
这样就容易计算了。至于
于是
考虑式子的后面两项,我们也可以用单调栈求出极长的区间
-
\forall j\in[L_i,i),a_j\le a_i -
\forall j\in(i,R_i],a_j<a_i -
\forall j\in[L_i,R_i],b_j\le a_i
然后和上面一样计算即可。
代码还是比较好写的。注意一些细节的处理,具体见代码。
预处理二的幂次即可做到
代码
#include<bits/stdc++.h>
#define int long long
#define pb push_back
#define SZ(x) (int)(x).size()
using namespace std;
const int N=1e5+5,mod=998244353,i2=(mod+1)/2;
int n,a[N],b[N],st[N],t,L[N],R[N],mi[N],ans;
inline int Mi(int x){return x<0?0:mi[x];}
inline int cal(int l,int m,int r){
if(l>m||m>r)return 0;
return (Mi(m-1)+mod-Mi(l-2))*(Mi(n-m)+mod-Mi(n-r-1))%mod;
}
inline void wrk1(){
for(int i=n;i;i--){
while(t&&a[i]>a[st[t]])L[st[t--]]=i+1;
st[++t]=i;
}
while(t)L[st[t--]]=1;
// 注意上下弹栈符号区别。
for(int i=1;i<=n;i++){
while(t&&a[i]>=a[st[t]])R[st[t--]]=i-1;
st[++t]=i;
}
while(t)R[st[t--]]=n;
for(int i=1;i<=n;i++)ans=(ans+cal(L[i],i,R[i])*a[i])%mod;
}
inline void wrk2(){
for(int i=n;i;i--){
while(t&&(b[i]>a[st[t]]||a[i]>a[st[t]]))L[st[t--]]=i+1;
if(a[i]>=b[i])st[++t]=i;else L[i]=i+1;
}
while(t)L[st[t--]]=1;
// 注意上下弹栈符号区别。
for(int i=1;i<=n;i++){
while(t&&(b[i]>a[st[t]]||a[i]>=a[st[t]]))R[st[t--]]=i-1;
if(a[i]>=b[i])st[++t]=i;else R[i]=i-1;
}
while(t)R[st[t--]]=n;
for(int i=1;i<=n;i++)ans=(ans+mod-cal(L[i],i,R[i])*a[i]%mod)%mod;
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);
cin>>n;
mi[0]=1;for(int i=1;i<=n;i++)mi[i]=(mi[i-1]*2)%mod;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i];
wrk1(),wrk2();
swap(a,b);
wrk1(),wrk2();
for(int i=1;i<=n;i++)ans=ans*i2%mod;
cout<<ans<<'\n';
return 0;
}
(逃