*/
题意:
给定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;
}
浙公网安备 33010602011771号