Problem J. Jim and the Skyscrapers(線段樹)

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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章