hdu 3450 Counting Sequences

*/
题意:
   给定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;

}


你可能感兴趣的:(sequence)