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



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;
}



你可能感兴趣的:(hdoj 3450 Counting Sequences 【离散化 + 树状数组优化dp】)