[ARC212E] Drop Min 题解
我们需要对满足条件的
也就是说,如果一个元素
由于这个性质类似一个“区间二选一”,所以普通的 DP 肯定是行不通的。这个时候再次审查这个结构,发现它形如“
重写一下条件:一个元素何时可以在
建出
- 若左边和右边都没有比它大的元素,则代表它是
P 中=N 的元素对应的结点;由于它最后留在了P 中(并没有进入A ),所以只用把两边以任意顺序归并起来而不用考虑自身,故k=C_{L+R}^{L} ; - 若仅有左边有比它大的元素,则所有左边的元素都必须出现得比
P_u 靠前:这个问题有个经典解法,从L+R+1 个位置中先选定R 个位置,把右边的R 个元素先固定在这些位置,于是左边的元素必然会在剩下位置中的前L 个(因为它们都要出现得比P_u 靠前),故k=C_{L+R+1}^{R} ; - 若仅有右边有比它大的元素,与上一种情况对称,故
k=C_{L+R+1}^{L} ; - 若两边都有元素比它大,先尝试简单地把上面两种情况对应的系数相加,发现可能会算重,会被算两遍的即为子树内所有元素(自身除外)都在
A 中比自身出现得靠前的方案数,为C_{L+R}^{L} ,扣掉就行了,故k=C_{L+R+1}^{R}+C_{L+R+1}^{L}-C_{L+R}^{L} 。
时间复杂度
放代码:
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N=2e5,P=998244353;
static int fac[N+1],fi[N+1];
inline int add(int x,int y){
int s=x+y; if(s>=P)s-=P; return s;
}
inline void self_add(int &x,int y){
if((x+=y)>=P)x-=P;
}
inline int qpow(int a,int b){
int r=1;
while(b){
if(b&1)r=1ll*r*a%P;
a=1ll*a*a%P,b>>=1;
}
return r;
}
inline int com(int n,int m){
return n<m?0:1ll*fac[n]*fi[m]%P*fi[n-m]%P;
} // 计算组合数
vector<pii> cartesian_tree(vector<int> p){
int n=p.size();
vector<pii> a(n,make_pair(-1,-1));
stack<pii> s;
for(int i=0;i<n;i++){
int l=-1;
while(!s.empty()&&s.top().second<p[i])
l=s.top().first,s.pop();
a[i].first=l;
if(!s.empty())a[s.top().first].second=i;
s.emplace(i,p[i]);
}
return a;
} // 建出笛卡尔树
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
for(int i=fac[0]=1;i<=N;i++)
fac[i]=1ll*fac[i-1]*i%P;
fi[N]=qpow(fac[N],P-2);
for(int i=N;i;i--)
fi[i-1]=1ll*fi[i]*i%P;
int n; cin>>n;
vector<int> p(n);
for(auto &i:p)cin>>i,i--;
auto s=cartesian_tree(p);
auto dfs=[&](auto &&self,int u,int l,int r,bool fl,bool fr)->int{
if(u<0)return 1;
int kl=self(self,s[u].first,l,u-1,fl,true),
kr=self(self,s[u].second,u+1,r,true,fr);
int c=0;
if(!fl&&!fr)c=com(r-l,u-l); // 两边都没有
if(fl)self_add(c,com(r-l+1,r-u)); // 左边有
if(fr)self_add(c,com(r-l+1,u-l)); // 右边有
if(fl&&fr)self_add(c,P-com(r-l,u-l)); // 两边都有,要扣掉算重的
return 1ll*kl*kr%P*c%P;
}; // 按照文中所述的方法进行 DP
int u=find(p.begin(),p.end(),n-1)-p.begin();
cout<<dfs(dfs,u,0,n-1,false,false)<<endl;
return 0;
}