【20200208】【yx】CF1295F dp

题意

给定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[i1][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 Clr2;同理对i+2的相同区间,贡献之和为 C l − r + 1 3 C_{l-r+1}^3 Clr+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)} (rl+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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值