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