*/
题意:
   给定N(N <= 100000)个数字ai和一个H,要求求出特殊序列的数量
,所谓特殊序列,就是相邻两个数字的绝对值小于等于H并且序列长度
大于等于2。

解法:
树状数组 + 动态规划

思路:
     首先我们利用dp[i]表示到第i个位置能够找到的相邻数字之差小
于等于H的长度大于等于1的序列的总和,那么有状态转移方程
dp[i] = sum{ dp[j], j<i, abs(a[j]-a[i]) <= H },这个做法的时间
复杂度是O(n^2),但是n很大,所以不能采用,但是我们观察到这个转移
方程是以求和的形式出现,并且有一个限制条件就是abs(a[j]-a[i])<=H
,我们可以把它简写成a[i]-H <= a[j] <= a[i]+H,那么如果我们把数
字映射到下标,并且通过二分找到a[j]的范围,就可以轻松的通过树状
数组的成段求和来统计了。
    具体做法是:由于数字较大,我们可以先将所有数字离散化,这样
每个数字就有一个 <= n 的标号,然后这个标号就可以对应树状数组的
下标了,每次从左往右在树状数组中统计[a[i]-H, a[i]+H]的解的数量
(注意,这里需要找到离散后对应的数字),然后将当前数字(离散后
的数字)插入到树状数组中,值即为先前找到的节的数量,循环结束,
累加和就是序列大于等于1的解的数量,然后再减去n就是最后的答案了
,这里注意是取模,并且保证答案不能为负数。
*/

code:

# include<stdio.h>
# include<string.h>
# include<stdlib.h>
# define N 100005
# define mod 9901
int a[N],b[N],count[N],k;
int cmp(const void *a,const void *b)
{
	return *(int *)a - *(int *)b;
}
int find(int x)
{
	//肯定能找到
	int left,right,mid;
	left=1;
	right=k;
	while(right>=left)
	{
		mid=(right+left)/2;
		if(b[mid]==x) return mid;
		if(b[mid]>x) right=mid-1;
		else left=mid+1;
	}
	return 0000000;
}
int find1(int x)
{
	//找到最小的大于等于x的
	int right,left,mid,ans;
	left=1;
	right=k;
	while(right>=left)
	{
		mid=(left+right)/2;
		if(b[mid]==x) return mid;
		if(b[mid]>x) {ans=mid;right=mid-1;}
		else left=mid+1;
	}
	return ans;
}
int find2(int x)
{
	//找到最大的小于等于x的
	int right,left,mid,ans;
	left=1;
	right=k;
	while(right>=left)
	{
		mid=(left+right)/2;
		if(b[mid]==x) return mid;
		if(b[mid]>x) right=mid-1;
		else {ans=mid;left=mid+1;}
	}
	return ans;
}
void insert(int num,int i)
{
	while(i<=k)
	{
		count[i]+=num;
		count[i]%=mod;
		i+=i&(-i);
	}
}
int query(int i)
{
	int sum=0;
	while(i>=1)
	{
		sum+=count[i];
		sum%=mod;
		i-=i&(-i);
		
	}
	return sum;
}
int main()
{
	int i,d,sum,ans,ans1,ans2,num,n;
	while(scanf("%d%d",&n,&d)!=EOF)
	{
		for(i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			b[i]=a[i];
		}
		qsort(b+1,n,sizeof(b[1]),cmp);
		k=1;
		for(i=2;i<=n;i++)
		{
			if(b[i]!=b[i-1]) b[++k]=b[i];
		}
		for(i=0;i<=n;i++)
			count[i]=0;
		sum=0;
		for(i=1;i<=n;i++)
		{
			ans=find(a[i]);
			ans1=find1(a[i]-d);
			ans2=find2(a[i]+d);
			num=(query(ans2)-query(ans1-1))%mod;
			if(num<0) num+=mod;
			sum+=num+1;
			sum%=mod;
			insert(num+1,ans);
		}
		printf("%d\n",((sum-n)%mod+mod)%mod);//要考虑结果不能为负数
	}
	return 0;
}


posted on 2011-09-28 17:28  奋斗青春  阅读(329)  评论(0编辑  收藏  举报