您的位置:首页 > 产品设计 > UI/UE

hdoj 3450 Counting Sequences 【离散化 + 树状数组优化dp】

2015-11-03 15:01 459 查看

Counting Sequences

Time 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

4 2
1 3 7 5

 

Sample Output

4

 

定义:一个序列中相邻两元素差的绝对值不大于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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: