題意:
給出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;
}