本沙茶的第一篇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; }