Problem J. Jim and the Skyscraper
題意:給定一個序列, 要你找出區間滿足:區間首尾元素相等且中間元素均小於首尾元素。要求你找出這樣的區間個數
解法:線段樹
首先存儲每個數字所有的出現位置,然後從最小的數開始遍歷(遍歷數字)
對於某個數字 如果出現的次數大於等於2
然後我們要做的事情是 將所有的位置進行劃分:
如果所有的位置之間任意兩個數都可以,則答案顯然是累加起來即可。
但如果出現中間存在大於,則顯然這個數先後的位置都不能構成區間對。
則對於每個兩個位置間進行查詢最大值,如果最大值大於當前數字,則進行分割。
區間查詢可以用線段樹完成。時間複雜度爲O(nlogn)
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn = 3e6 + 10;
vector<int> g[2000000];
int a[maxn];
int n, dat[2 * maxn + 100];
void init(int n_)
{
n = 1;
while(n < n_)
n *= 2;
for(int i = 0; i < 2 * n - 1; i++)
dat[i] = INT_MIN;
}
void update(int k, int a)
{
k += n - 1;
dat[k] = a;
while(k > 0)
{
k = (k - 1) / 2;
dat[k] = max(dat[k * 2 + 1], dat[k * 2 + 2]);
}
}
int query(int a, int b, int k, int l, int r)
{
if(r <= a || b <= l) return INT_MIN;
if(a <= l && r <= b) return dat[k];
else
{
int vl = query(a, b, k * 2 + 1, l, (l + r) / 2);
int vr = query(a, b, k * 2 + 2, (l + r) / 2, r);
return max(vl, vr);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
init(t);
int mmax = 0, mmin = 3e6 + 10;
for(int i = 0; i < t; i++)
{
cin >> a[i];
update(i, a[i]);
g[a[i]].push_back(i);
mmax = max(a[i], mmax);
mmin = min(a[i], mmin);
}
ll cnt = 0;
for(int i = mmin; i <= mmax; i++)
{
if(g[i].size() >= 2)
{
int len = g[i].size();
ll now = 0;
for(int j = 0; j < len - 1; j++)
{
int q = query(g[i][j], g[i][j + 1], 0, 0, n);
if(q > i)
{
cnt += (now + 1) * now / 2;
now = 0;
}
else
{
now++;
if(j == len - 2)
cnt += (now + 1) * now / 2;
}
}
}
}
cout << 1LL * cnt * 2 << endl;
return 0;
}