Codeforces Round #266 (Div. 2) Problem D Solution

本沙茶的第一篇blog.

题目大意:

一个长度为N的整数序列a,现在要对若干个区间[l,r]进行操作,使得区间里的每一个数均加1.而且对于任意两个区间[l1,r1],[l2,r2],满足l1!=r1&&l2!=r2.

现在想要在操作后使序列中的每个数均变为h,求不同的方案数。

两个方案不同当且仅当区间选取情况不完全相同。

N<=2000,a1,a2,...an,h<=2000.


Sol:

首先我们很容易处理出若想要到达h,原序列中的每个数应该加上多少。不妨记为新序列a'.

若a'中存在负数,显然答案为0.

否则如果a'为全0序列,答案为1.

否则,我们取出a'中的若干个连续正数序列,分别处理。

对于某个正数序列,若头或尾不为1,则答案为0.


注意观察,我们能够发现一些隐藏的性质:相邻两个数的差的绝对值不超过1.若不满足此性质,则答案为0.

怎么证明呢?

考虑位置i+1,若位置i及其之前已经存在一种合法的区间覆盖方法,那么位置i一定被a'[i]个区间覆盖。

那么这些区间可以继续向前延伸去覆盖位置i+1,但是也可能在这些区间中某个区间以位置i结尾(至多只能有一个),那么位置i+1的可能覆盖区间数就为a'[i],a'[i]-1.

但是也可能有以位置i+1开头的区间覆盖位置i+1(此时覆盖数+1),也可能没有。

因此,存在三种可能a'[i]-1,a'[i],a'[i]+1.

我们分别讨论三种情况的方案数。

若a'[i+1]=a'[i]-1,那么只可能是a'[i]个区间中有一个区间以位置i结尾,我们从中任取即可,方案数为a'[i].

若a'[i+1]=a'[i],那么有可能是a'[i]个区间全部向前延伸覆盖i+1,此时一种方案;或者有1个以位置i+1开头的区间覆盖位置i+1,之前的a'[i]个区间中有一个结尾为i,方案数为a'[i].因此总方案数为a'[i]+1.

若a'[i+1]=a'[i]+1,那么只可能是a'[i]个区间全部向前延伸覆盖i+1,而且有1个以位置i+1开头的区间覆盖位置i+1,1种方案。

注意到每一个位置的选法都是相互独立的,于是利用乘法原理处理即可。


连续正数序列之间也是相互独立的,同样利用乘法原理。

因此,总的时间复杂度为O(n).


Code:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <iostream>
#include <algorithm>
using namespace std;

#define N 2010
int a[N];

#define mod (1000000007)
#define _abs(x) ((x)>0?(x):(-(x)))
int main() {
    int n, h;
    scanf("%d%d", &n, &h);
    register int i, j, k;
    for(i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        if (a[i] > h) {
            puts("0");
            return 0;
        }
        a[i] = h - a[i];
    }
    
    bool ac = 1;
    for(i = 1; i <= n; ++i)
        if (a[i]) {
            ac = 0;
            break;
        }
    if (ac) {
        puts("1");
        return 0;
    }
    
    long long res = 1;
    for(i = 1; i <= n; ++i) {
        if (a[i]) {
            for(j = i; a[j]; ++j);
            --j;
            if((a[i] != 1) || (a[j] != 1)) {
                puts("0");
                return 0;
            }
            for(k = i; k < j; ++k)
                if (_abs(a[k] - a[k + 1]) > 1) {
                    puts("0");
                    return 0;
                }
            for(k = i; k < j; ++k) {
                if (a[k] == a[k + 1])
                    res = res * (a[k] + 1) % mod;
                else if (a[k + 1] == a[k] - 1)
                    res = res * a[k] % mod;
            }
            i = j;
        }
    }
    
    printf("%I64d", res);
    
    return 0;
}


你可能感兴趣的:(Codeforces Round #266 (Div. 2) Problem D Solution)