[APIO2016][组合计数dp]划艇

一边探索,一边破坏

                        ——《巨齿鲨》

离散成一些开区间;/*如果计数题数大区间少可以考虑这个思路,用组合数计数处理区间*/

用f[i][j][k]表示最后一个取到i这个学校,取值在第j段的,j段里已经取了k个的方案数;

转移,考虑前一个取到的学校;

f[i][j][k]=f[i−1][j][k]+f[i−1][j][k−1]∗(len[j]−k+1)/k
f[i][j][1]=f[i−1][j][1]+∑∑f[i−1][j′][k]∗len[j]
其中len[j]表示第j段包含整点个数。 
用前缀和优化第二个转移   {注意这个区间处理,右边部分+1,小处理避免重复}  {同时,前缀和优化只要对每个j记录一个值即可,                                                                                                                                   想一想,处理起来麻烦就简化了,事半功倍}

#include
#define rep(i, x, y) for(int i = (x); i <= (y); i++)
#define per(i, x, y) for(int i = (x); i >= (y); i--)
#define N 505
#define ll long long
#define mod 1000000007
using namespace std;
int n, tot, a[N], b[N], num[N<<1], g[N], C[N], inv[N];
int main() {
    scanf("%d", &n);
    inv[1] = 1; rep(i, 2, n) inv[i] = (ll)(mod-mod/i)*inv[mod%i]%mod;
    rep(i, 1, n) {
        scanf("%d%d", &a[i], &b[i]);
        num[++tot] = a[i]; num[++tot] = b[i]+1;
    }
    sort(num+1, num+1+tot); tot = unique(num+1, num+1+tot)-num-1;
    rep(i, 1, n) {
        a[i] = lower_bound(num+1, num+1+tot, a[i])-num;
        b[i] = lower_bound(num+1, num+1+tot, b[i]+1)-num;
    }
    C[0] = 1; g[0] = 1;
    rep(j, 1, tot-1) {
        int len = num[j+1]-num[j];
        rep(i, 1, n) C[i] = (ll)C[i-1]*(len+i-1)%mod*inv[i]%mod;
        per(i, n, 1) if(a[i] <= j && j+1 <= b[i]) {
            int f = 0, m = 1, c = len;//m是i-p
            per(p, i-1, 0) {
                f = (f+(ll)c*g[p]%mod)%mod;
                if(a[p] <= j && j+1 <= b[p]) c = C[++m];
            } g[i] = (g[i]+f)%mod;
        }
    }
    int ans = 0; rep(i, 1, n) ans = (ans+g[i])%mod;
    printf("%d\n", ans);
    return 0;
}

 

 

你可能感兴趣的:(计数,计数dp)