Counting SequencesTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/65536 K (Java/Others)Total Submission(s): 2128 Accepted Submission(s): 736
Problem Description
For a set of sequences of integers{a1,a2,a3,...an}, we define a sequence{ai1,ai2,ai3...aik}in which 1<=i1<i2<i3<...<ik<=n, as the sub-sequence of {a1,a2,a3,...an}. It is quite obvious that a sequence with the length n has 2^n sub-sequences. And for a sub-sequence{ai1,ai2,ai3...aik},if it matches the following qualities: k >= 2, and the neighboring 2 elements have the difference not larger than d, it will be defined as a Perfect Sub-sequence. Now given an integer sequence, calculate the number of its perfect sub-sequence.
Input
Multiple test cases The first line will contain 2 integers n, d(2<=n<=100000,1<=d=<=10000000) The second line n integers, representing the suquence
Output
The number of Perfect Sub-sequences mod 9901
Sample Input
Sample Output
|
定义:一个序列中相邻两元素差的绝对值不大于k -> 该序列是一个关于k的完美序列,要求元素个数不少于2。
题意:给定n个元素组成的序列,问你关于d的完美子序列有多少个。
花了整整三个小时,终于AC了。
/(ㄒoㄒ)/~~ 因为ans %= MOD 可能出现负数,这点bug找了一个多小时。 醉了
思路:定义dp[i]表示以a[i]结尾的关于d的完美子序列的个数,可以把只有一个元素的情况也考虑进去。
容易得到dp[i] = sigma(dp[j]) + 1,其中(1 <= j < i && abs(a[j] - a[i]) < d)。
这样时间复杂度为O(n*2),但是题目时限为1s,复杂度过高。
考虑用一种数据结构来优化 sigma(dp[j])。可以先考虑特殊情况——即元素按照升序排列。
那么对于dp[i]则有dp[i] = dp[l] + ... + dp[r]。
定义l:对任意的a[l](1 <= l <= n),找不到一个a[j]使得a[j] >= a[i] - d && a[j] < a[l]。
定义r:对任意的a[r](1 <= r <= n),找不到一个a[j]使得a[j] <= a[i] + d && a[j] > a[r]。
把序列元素升序排列,又不丢失序列元素的顺序,这点可以用离散化实现。策略是对于每个元素a[i],找到离散化后a[l]、a[r]、a[i]的id值l、r、p,然后dp[p] = dp[l] + ... + dp[r]来更新结果。
至于dp[l] + ... + dp[r],线段树、树状数组都可以解决,不过还是树状数组比较方便。
时间复杂度O(nlog(n))
注意:(1)取余操作中可能出现负数 (2)最后结果减去n,因为我们把一个元素的情况也考虑进去了。
AC代码:
#include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <algorithm> #include <queue> #include <stack> #include <map> #include <vector> #define INF 0x3f3f3f3f #define eps 1e-8 #define MAXN 100000+10 #define MAXM 50000000 #define Ri(a) scanf("%d", &a) #define Rl(a) scanf("%lld", &a) #define Rs(a) scanf("%s", a) #define Pi(a) printf("%d\n", (a)) #define Pl(a) printf("%lld\n", (a)) #define Ps(a) printf("%s\n", (a)) #define W(a) while(a--) #define CLR(a, b) memset(a, (b), sizeof(a)) #define MOD 9901 #define LL long long using namespace std; int lowbit(int x){ return x & (-x); } int n, d; int dp[MAXN]; void update(int x, int add) { while(x <= n) { dp[x] += add; dp[x] %= MOD; x += lowbit(x); } } int sum(int x) { int s = 0; while(x > 0) { s += dp[x]; s %= MOD; x -= lowbit(x); } return s; } int rec[MAXN]; int Findleft(int val, int l, int r) { int ans; while(r >= l) { int mid = (l + r) >> 1; if(rec[mid] >= val) { r = mid-1; ans = mid; } else l = mid+1; } return ans; } int Findright(int val, int l, int r) { int ans; while(r >= l) { int mid = (l + r) >> 1; if(rec[mid] <= val) { l = mid+1; ans = mid; } else r = mid-1; } return ans; } int Findpos(int val, int l, int r) { while(r >= l) { int mid = (l + r) >> 1; if(rec[mid] == val) return mid; else if(rec[mid] > val) r = mid-1; else l = mid+1; } } int a[MAXN]; int main() { while(scanf("%d%d", &n, &d) != EOF) { int len = 1; for(int i = 1; i <= n; i++) { Ri(a[i]); rec[len++] = a[i]; } sort(rec+1, rec+len); int R = 2; for(int i = 2; i < len; i++) if(rec[i] != rec[i-1]) rec[R++] = rec[i]; sort(rec+1, rec+R); CLR(dp, 0); int ans = 0; for(int i = 1; i <= n; i++) { int l = Findleft(a[i]-d, 1, R-1); int r = Findright(a[i]+d, 1, R-1); int p = Findpos(a[i], 1, R-1); int sub = (sum(r) - sum(l-1) + 1)%MOD; ans += sub-1; ans = (ans + MOD) % MOD;//注意可能出现负数 update(p, sub); } Pi(ans); } return 0; }