永恆的契約

題目大意

有一個神奇的大回環,由n塊石頭組成。
第i塊石頭有一個高度ai,兩塊不同的石頭i,j能夠互相看到,則它們在環上的兩條路徑中有至少一條路徑上除了兩個端點(即i,j)路徑上石頭高度都不大於min(ai,aj)。
求有多少對石頭能夠互相看到

數據範圍n<=1e6,T<=5,1<=ai<=1e9

先不考慮ai=aj的情況

我們可以把序列加倍,然後用單調棧處理出l[i]和r[i](i滿a[j]a[i]j) ,也就是隻考慮i能看到a值比自己大的,同時如果i左右看到位置在原序列中相同ans只+1否則,這樣就不會重複計算了。
ai=aj時,這個討論起來不會很麻煩,處理l[i]的同時便可一起維護。

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define ll long long
using namespace std;
const int maxn=2e6+5;
int i,j,n,m,t,a[maxn],l[maxn],r[maxn],f[maxn];ll ans,an[maxn];
void read(int &x){
    x=0;char c=getchar();
    while ((c<'0')||(c>'9')) c=getchar();
    while ((c>='0')&&(c<='9')) x=x*10+c-'0',c=getchar();
}
int main(){
    freopen("forever.in","r",stdin);
    freopen("forever.out","w",stdout);
    read(t);
    while (t>0){
        t--;read(n);
        fo(i,1,n+n) an[i]=0;
        fo(i,1,n) read(a[i]),f[i]=i;
        fo(i,n+1,n+n) a[i]=a[i-n];ans=0;
        fo(i,1,n+n) {
            j=i-1;
            while ((j>0)&&(a[j]<=a[i])) {
                if (a[j]==a[i]) {
                    if (i<=n) f[i]=f[j],an[f[i]]++,ans+=an[f[i]];else{
                        if (j<=n&&f[j]!=i-n) ans+=(ll)(an[f[j]]+1)*(an[f[i-n]]+1);
                    }
                } 
                j=l[j];
            }l[i]=j;
        }
        fod(i,n+n,1){
            j=i+1;
            while ((j<=(n+n))&&(a[j]<=a[i])) j=r[j];
            r[i]=j;
        }
        fo(i,1,n) if (r[i]<=n+n) ans++;
        fo(i,n+1,n+n) if (l[i]>0){
            if (r[i-n]!=l[i])ans++;
        }
        printf("%lld\n",ans);
    }
}
發佈了80 篇原創文章 · 獲贊 21 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章