题意:
给出n 和 d (2<=n<=100000,1<=d=<=10000000), 定义,任意一个子串,如果满足相邻元素的差不大于d,则称该子串为一个perfect子串,问一共有多少这样的子串,输出ans % mod。
题解:
设dp(i)表示前i个字符中,perfect子串的个数,则dp[i] = sum(dp[j], 0<j<i, |s[j]-s[i]| <= d), 这题的关键点在于如何快速求出sum(),可考虑用树状数组,则,sum(i+d) - sum(i-d-1) 就是答案,可是问题是:1.数字范围很大。2.可能存在负数。解决这两个问题可以考虑用离散化,如何离散化呢?其实我们只需要把dp[i]在树状数组中的位置离散化就行了,只需要定义一个dis[]数组,里面保存的是排序并且去重后的原数组,那么,只需在dis[]数组中进行二分查找即可找到位置了。
细节:
这题有个小陷阱,就是取模的时候,要注意可能为负数。
具体就看代码吧:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 10;
const int MOD = 9901;
int arr[maxn], dis[maxn], c[maxn], n, d, cnt;
//6 2
//-5 -7 0 3 1 2
void read_input()
{
for (int i = 1; i <= n; i++)
{
scanf("%d", &arr[i]);
dis[i] = arr[i];
}
}
void dis_cre()
{
sort(dis+1, dis+1+n);
cnt = 0;
for (int i = 1; i <= n; i++)
{
if (i != 1 && dis[i] == dis[i-1])
{
dis[cnt] = dis[i];
}
else {
dis[++cnt] = dis[i];
}
}
}
void init()
{
memset(c, 0, sizeof(c));
}
inline int lowbit(int x) { return x & (-x); }
void add(int x, int v)
{
while (x <= cnt)
{
c[x] += v;
x += lowbit(x);
}
}
int sum(int x)
{
int ans = 0;
while (x > 0)
{
ans += c[x];
ans %= MOD;
x -= lowbit(x);
}
return ans;
}
int bi_search(int v)
{
int L = 1, R = cnt;
while (L <= R)
{
int M = (L + R) >> 1;
if (dis[M] < v) {
L = M+1;
}
else {
R = M-1;
}
}
if (dis[L] == v) return L;
return R;
}
void solve()
{
int dp = 1, ans = 1, index = bi_search(arr[1]);
add(index, dp);
for (int i = 2; i <= n; i++)
{
index = bi_search(arr[i]);
int L = bi_search(arr[i]-d-1);
int R = bi_search(arr[i]+d);
dp = sum(R) - sum(L) + 1;
if (dp < 0) dp += MOD;
ans += dp;
ans = ans % MOD;
add(index, dp);
}
ans = ((ans-n)%MOD+MOD) % MOD;
printf("%d\n", ans);
}
int main()
{
// freopen("/Users/apple/Desktop/in.txt", "r", stdin);
while (scanf("%d%d", &n, &d) != EOF)
{
init();
read_input();
dis_cre();
solve();
}
return 0;
}