组合数 (选定思想) dp cf1000D - Yet Another Problem On a Subsequence

http://codeforces.com/problemset/problem/1000/D

参考自:https://www.cnblogs.com/hua-dong/p/9238795.html

题意:给定序列,问有多少子序列(不一定连续),满足可以划分为若干个组,给个组的第一个等于区间长度-1;

思路:关键就在于区间的开头,由此我们从后往前考虑,dp[ i ] 表示以 i 开头,满足题意的数量,sum[ i ] 表示 i 及以后所有的可能情况。

对于 i ,可选的区间为 [ i , j ] , j 的范围是 i+a[ i ] ~ n,再利用选定的思想,这一段区间的可能情况就有 C (  a[ i ] -1 , j - i -1 ) ,接着,再乘上(1+sum[ j +1 ]),

这就是对乘法思想的利用,而这个 1 代表只选前面一个段的情况,通过不断的累加就得到 dp[ i ],再累加就得到 sum[ i ]。

 

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
const int Mod=998244353;
const int maxn=1010;
int a[maxn],dp[maxn],sum[maxn];
int c[maxn][maxn],ans;
int main()
{
    int N,i,j;
    scanf("%d",&N);
    for(i=0;i<=N;i++) c[i][0]=1,c[i][1]=i,c[i][i]=1;
    for(i=1;i<=N;i++)
     for(j=1;j<=N;j++)
       c[i][j]=(c[i-1][j]+c[i-1][j-1])%Mod;
    for(i=1;i<=N;i++) scanf("%d",&a[i]); 
    for(i=N;i>=1;i--){
        if(a[i]>0&&i+a[i]<=N){
            for(j=i+a[i];j<=N;j++){
                (dp[i]+=(ll)c[j-i-1][a[i]-1]*(1+sum[j+1])%Mod)%=Mod;  //这里的1是指只有一个段的情况
                printf("%d %d \n", c[j-i-1][a[i]-1],(1+sum[j+1]));
            }
        }
        sum[i]=(sum[i+1]+dp[i])%Mod;
        // printf("%d\n", sum[i]);
    }
    printf("%d\n",sum[4]);
    return 0;
}









// #include <iostream>
// #include <cstdio>
// #include <cstdlib>
// #include <cstring>
// #include <cmath>
// #define qwq(i,j,k)      for(int i=j; i<k; ++i)
// #define qeq(i,j,k)      for(int i=j; i<=k; ++i)
// #define mem(a,b)        memset(a, b, sizeof(a))
// using namespace std;
// typedef long long ll;
// const int mx = 1e5+5;
// const int maxn = 3e3+5;
// const int inf= 0x3f3f3f3f;
// // const ll mod = 1e9+7;
// const ll INF = 1e18+100;
// int n, m;
// int cas, tol, T;

// ll C[maxn][maxn];
// ll fac[mx]; //i!
// ll inv[mx]; //i!在取模mod的情况下的逆元

// // ll fpow(ll a, ll b) {
// //     ll ans = 1;
// //     while(b) {
// //         if(b & 1)
// //             ans = ans * a % mod;
// //         a = a * a % mod;
// //         b >>= 1;
// //     }
// //     return ans;
// // }

// const ll mod=998244353;
// void handle() {
// //    fac[0] = 1;
// //    for(int i=1; i<=mx; ++i) {
// //        fac[i] = fac[i-1]*i%mod;    //阶乘的表
// //    }
// //    inv[mx] = fpow(fac[mx], mod-2);
// //    for(int i=mx-1; i>=1; --i) {
// //        inv[i] = inv[i+1] * (i+1) % mod;
// //    }
//     C[0][0] = 1;
//     C[1][0] = C[1][1] = 1;
//     for(int i=2; i<maxn; ++i) {
//         for(int j=0; j<=i; ++j) {
//             C[i][j] = j==0 ? 1 : (C[i-1][j]%mod+C[i-1][j-1]%mod)%mod;
//         }
//     }
// }

// ll a[maxn];
// ll b[maxn];
// ll ans;

// int main() {
//     //打表O(N^2)   n 1e3左右
//     handle();
//     // for(int i=1; i<=10; ++i) {
//     //     for(int j=0; j<=i; ++j) {
//     //         printf("C[%d][%d] = %lld\n", i, j, C[i][j]);
//     //     }
//     // }
//     ans=0;
//     scanf("%d",&n);
//     for(int i=1; i<=n; i++){
//         cin>>a[i];
//     }
//     for(int i=1; i<=n; i++){
//         if(a[i]>0 && a[i]+i<=n){
//             b[i] = C[n-i][ a[i] ];
//         }
//     }
//     for(int i=n-1; i>=1; i--){
//         if(b[i]!=0){
//             ll sum=0;
//             for(int j=n-1; j>=i+a[i]+1; j--){
//                 // if(a[j]>0 && a[j]+j<=n){
//                     b[i] += b[j];

//                 // }
//               // if(i==4) printf("b[4]=%lld\n", b[i]);
//             }
//             // b[i] = (b[i]%mod + b[ i+a[i]+1 ]%mod)%mod;
//             ans = (ans+b[i])%mod;
//         }   
//     }
//     printf("%d\n", b[2]);
//     cout<<ans<<endl;

//     return 0;
// }

 

转载于:https://www.cnblogs.com/-Zzz-/p/11427423.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值