树状数组
很显然是动态规划。dp[i]表示前i个数有多少个有效的子序列。那么 dp[i]=dp[i-1]+A。 A是前面i-1个数中,与i的差值不超过d的以该数结尾的有效的子序列的个数 的和。我们可以用另外一个数组sub[i]表示以i结尾的有效的子序列的个数。 dp与sub的不同之处是dp中的子序列不一定是以第i个数结尾的。
sub[i]= sigma sub[k] ,( abs(numk],num[i])<=d )。 由于求sub的时间复杂度为O(n^2),而n太大,因此需要离散化后用树状数组。
树状数组求和是一段连续的,而sub要求和的是位于区间[num[i]-d,num[i]+d],所以要对num排序,这样就能把
[num[i]-d,num[i]+d]放到连续的区间中。
HDU 3450
#include <iostream>
#include <algorithm>
#include <string>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
const int MOD = 9901;
const int N = 1e5 + 5;
int n,dp[N],cnt[N],tree[N];
struct Node
{
int index,value;
bool operator < (const Node &x)const
{
return value < x.value;
}
}num[N];
static inline int lowbit(int t)
{
return(t&(-t));
}
void Update(int i,int d)
{
d %= MOD;
for(;i <= n;i += lowbit(i)) tree[i] = (tree[i]+d)%MOD;
}
int GetSum(int i)
{
int s = 0;
for(;i > 0;i -= lowbit(i)) s = (s+tree[i])%MOD;
return s;
}
int Find(int k)
{
int mid,ret = 0,left = 1,right = n;
while(left <= right)
{
mid = (left+right)>>1;
if(num[mid].value <= k){ // 求上界
left = mid + 1;
ret = mid;
}
else
right = mid - 1;
}
return ret;
}
int main()
{
int d;
while(~scanf("%d%d",&n,&d))
{
for(int i = 1;i <= n;++i)
{
scanf("%d",&num[i].value);
num[i].index = i;
}
sort(num+1,num+1+n);
for(int i = 1;i <= n;++i)
{
cnt[num[i].index] = i;
}
memset(tree,0,sizeof(tree));
Update(cnt[1],1); //预处理第一个点,题目要求至少两个点
dp[1] = 0;
for(int i = 2;i <= n;++i)
{
int k1 = Find(num[cnt[i]].value+d); //其中的i,就是原本输入时的索引.而cnt[i]为排序后的位置(离散化后)
int k2 = Find(num[cnt[i]].value-d-1);
int tmp = GetSum(k1) - GetSum(k2);
tmp = (tmp%MOD+MOD)%MOD;
dp[i] = (dp[i-1]+tmp)%MOD;
Update(cnt[i],tmp+1); //cnt[i]位置满足条件的有几个
}
printf("%d\n",dp[n]);
}
return 0;
}
/*
因为题目要求的是,按原来序列中的顺序找到子序列。而经过排序后原来的位置将有可能全部打乱。
此时要怎么办才可以在即运用树状数组的前提下,又可以满足题目的要求呢?
我们此时就会想到了运用离散化的思想。
为什么这个可行呢?
因为,我们对value排序后,虽然索引是打乱了,但是我们之前记住了输入时的位置。
所以,我们可以根据记录的index来找回输入时的位置。
刚接触很难理解,我也是如此。下面就来举个例子。
比如,num[i].index = 2;而排序后的i值可能为i = 4;
而调用cnt[2] = 4;(表示当输入时下标为2的,经过了排序后位置变为了4)
而我们在统计的时候,我们还是要根据之前的下标位置来进行。
还是上面一个例子,当我们要统计第二个输入的数时候,我们怎么知道他在排序后的位置是在哪里
呢?
此时我们调用pos = cnt[2],pos即使排序后的位置,然后我们将pos传给num[pos].value就可以了。
这样我们就证明完了离散化后的正确性。
*/
什么是离散化?
排序后处理/对坐标的近似处理
基本思想:只考虑需要用的值。
作用:有效的降低时间复杂度。