題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6701
解題心得:
- 這個題首先要明白是用分治去解決,也就是說如果區間之間最大值在位置,那麼可以枚舉當左端點爲得到包含的右端點(固定左端點算貢獻),按照題目中所給的關係式和不重複的規定得到符合條件右端點爲一段區間,如果,那麼說明這個點對於最後的答案有貢獻,那麼最終答案加上()。算出當前之後遞歸下去計算和
- 其次有個問題就是怎麼可以直接找到區間的最大值和最大值的位置,這個可以先處理出一個ST表,這樣就可以直接得到了。並且關於一點的不重複左右區間也可以先預處理出來。
- 最後分治的時候有個問題就是當所給出的個數爲不重複升序排列的時候,那麼分治僅僅是每次將右端點向左移動一位並且每次枚舉固定左端點複雜度就成了。這個時候就需要啓發式優化,也就是說我們找到區間之間最大值在位置,那麼算貢獻的時候可以固定左端點計算右端點的個數,也可以固定右端點計算左端點的個數,固定左/右端點需要枚舉的區間分別可以是和,那麼假設枚舉規則是哪個區間小枚舉哪個區間,這時遞歸下去的時候複雜度越壞計枚舉算次數越少,這就將最壞的情況進行了限制,真的是非常精妙的優化啊。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5+100;
int num[maxn], maxSt[maxn][30], posSt[maxn][30], dR[maxn], dL[maxn], n, k;
ll ans = 0;
void init() {
ans = 0;
scanf("%d%d", &n, &k);
for(int i=1;i<=n;i++) scanf("%d", &num[i]);
}
void getStTable() {
for(int i=1;i<=n;i++) {
maxSt[i][0] = num[i];
posSt[i][0] = i;
}
for(int j=1;j<=30;j++) {
for(int i=1;i+(1<<j)-1<=n;i++) {
int Max1 = maxSt[i][j - 1];
int Max2 = maxSt[i + (1 << (j - 1))][j - 1];
if (Max1 >= Max2) {
maxSt[i][j] = Max1;
posSt[i][j] = posSt[i][j - 1];
} else {
maxSt[i][j] = Max2;
posSt[i][j] = posSt[i+(1<<(j-1))][j-1];
}
}
}
}
bool vis[maxn];
void getLRPos() {
int pos = 2;
vis[num[1]] = true;
for(int i=1;i<=n;i++) {
while(pos <= n && !vis[num[pos]]) {
vis[num[pos]] = true;
pos++;
}
dR[i] = pos-1;
vis[num[i]] = false;
}
memset(vis, 0, sizeof vis);
pos = n-1;
vis[num[n]] = true;
for(int i=n;i>=1;i--) {
while(pos >= 1 && !vis[num[pos]]) {
vis[num[pos]] = true;
pos--;
}
dL[i] = pos+1;
vis[num[i]] = false;
}
}
int getPos(int l, int r) {
int Log = 0, Pow = 1;
while(Pow <= (r-l+1)) {
Pow <<= 1;
Log++;
}
Log--;
int Max1 = maxSt[l][Log];
int Max2 = maxSt[r-(1<<Log)+1][Log];
if(Max1 >= Max2) return posSt[l][Log];
else return posSt[r-(1<<Log)+1][Log];
}
void getCnt(int l, int r) {
if(l > r) return ;
int mid = getPos(l, r);
if(r - mid > mid - l) {
for (int i = l; i <= mid; i++) {
int L = max(mid, num[mid] - k + i - 1);
int R = min(dR[i], r);
if (L <= R) ans += R - L + 1;
}
} else {
for(int i=r;i>=mid;i--) {
int R = min(mid, k+1+i-num[mid]);
int L = max(dL[i], l);
if(L <= R) ans += R-L+1;
}
}
getCnt(l, mid-1);
getCnt(mid+1, r);
}
int main() {
// freopen("1.in.txt", "r", stdin);
int t; scanf("%d", &t);
while(t--) {
init();
getStTable();
getLRPos();
getCnt(1, n);
printf("%lld\n", ans);
}
}