题意
给定n个区间(l,r)(n<=50,1<=l,r<=998244353),包含两侧,从n个区间中各取一个数,求能形成的长度为n的严格递减序列个数
思路
注意到n的范围很小,考虑将区间按端点值分为更小的区间,记dp[i][j]为从右向左(递增)选择到第i个数时取了 j 的序列数,则有
d
p
[
i
]
[
j
]
=
∑
d
p
[
i
−
1
]
[
k
]
(
l
i
<
k
<
j
)
dp[i][j]=\sum{dp[i-1][k](l_i<k<j)}
dp[i][j]=∑dp[i−1][k](li<k<j)
对一段小区间(l,r),计算 i=1 时的区间整体对i的各区间的贡献,该区间内的点对i+1 时的相同区间贡献为(r-l-1),(r-l-2),…1,贡献之和为
C
l
−
r
2
C_{l-r}^2
Cl−r2;同理对i+2的相同区间,贡献之和为
C
l
−
r
+
1
3
C_{l-r+1}^3
Cl−r+13…
对l’>r的区间(l’,r’),贡献为
(
r
−
l
+
1
)
∑
d
p
[
i
]
[
j
]
(
l
<
=
j
<
=
r
)
(r-l+1)\sum{dp[i][j](l<=j<=r)}
(r−l+1)∑dp[i][j](l<=j<=r),答案可表示为一些组合数相加的形式
从i=1时dp求解,复杂度O(n^4)/ O(n^3)(预处理组合数)
//前两天补的题有点水,就不当成打卡来写啦
#include <bits/stdc++.h>
#define ll long long
#define tp(x) std::cout<<"& "<<(x)<<" &\n"
using namespace std;
const int p=998244353;
int cnt=1,tot,n;
struct kk{
ll l,r;
}seg[55];
ll qpow(ll a,int n){
ll ans=1;
while(n){
if(n&1){
ans*=a;ans%=p;
}
a*=a;a%=p;
n>>=1;
}
return ans;
}
ll invv[60],bnd[110],dp[120][120];
void pre(){
invv[1]=1;
for(int i=2;i<=55;++i){
invv[i]=invv[i-1]*i%p;
invv[i-1]=qpow(invv[i-1],p-2);
}
invv[55]=qpow(invv[55],p-2);
sort(bnd+1,bnd+tot+1);
tot=unique(bnd+1,bnd+tot+1)-bnd-1;
for(int i=1;i<=n;++i){
seg[i].l= lower_bound(bnd + 1, bnd + tot + 1, seg[i].l) - bnd;
seg[i].r= lower_bound(bnd + 1, bnd + tot + 1, seg[i].r + 1) - bnd;
}
}
inline ll C(int a,int b){
ll ans=1;
for(int i=a;i>a-b;--i)ans=(ans*i)%p;
ans=ans*invv[b]%p;
return ans;
}
int main(){
ll inv=1;
scanf("%I64d",&n);
for(int i=1;i<=n;++i){
scanf("%I64d%I64d", &seg[cnt].l, &seg[cnt].r);
inv= (inv*qpow(seg[i].r - seg[i].l + 1, p - 2)) % p;
bnd[++tot]=seg[cnt].l;bnd[++tot]= seg[cnt++].r + 1;
}
pre();
for(int i=1;i<=tot;++i)dp[0][i]=1;
for(int i=1;i<=n;++i){
for(int j=seg[i].l; j < seg[i].r; ++j){
for(int k=i;k>0;--k){
if(j < seg[k].l || j >= seg[k].r)break;
dp[i][j]=(dp[i][j]+dp[k-1][j+1]*C(i-k+bnd[j+1]-bnd[j],i-k+1))%p;
// cout<<i-k+bnd[j+1]-bnd[j]<<" "<<i-k+1<<endl;
}
}
for(int j=tot;j>=1;--j)dp[i][j]=(dp[i][j]+dp[i][j+1])%p;//
}
printf("%I64d\n",dp[n][1]*inv%p);
return 0;
}