51Nod-1482 部落信号(单调栈)

题意

一个长度为 n 的环 a ,如果环上有一对数 x , y ,当连接 x , y 的两对弧中任意一对上不存在比 m i n { x , y } 大的数,则称 x , y 可以互相看见,问有多少对数可以互相看见。
3 n 10 6

思路

首先,与环有关的问题,最基本的思路就是段环成链。因为连接 x , y 的圆弧,一定不必穿过环上的最大值,所以可以去掉环上最大值并把环拆成链,最后重新算一遍最高点的贡献即可。不难发现,将 x 作为较低点,它能连结的点有左边第一个比它大的点和右边第一个比它大的点,于是想到单调栈。但是这样有一个问题,如果单调栈中出现了相同元素,那么点 x 还能连接到所有与 x 大小相同的点。所以还要特殊处理这一类点。单调栈进行的过程中始终保持序列的递减,那只用用 x 替换栈内元素时,维护一个 s 数组如果遇到相同元素,则通过累加得到与 x 相同的元素个数即可。准确的说,是 x 向左边拓展,在遇到比它大的点时停下,中途遇到与 x 相等的元素的个数(当然,除了将所有数的 s 值加起来,也可以加上所有的 C k 2 ,其中 k 为一个满足上述要求,值相等的点集中点的个数)。

代码

由于这题有些卡语言,所以用 C 语言交了

#include<stdio.h>
#define FOR(i,x,y) for(register int i=(x);i<=(y);++i)
#define DOR(i,x,y) for(register int i=(x);i>=(y);--i)
#define N 1000003
typedef long long LL;
int mark[N];
int a[N],t[N],s[N],n;
int stk[N],top;
int p,mx=-1,h;
LL ans;

int main()
{
    scanf("%d",&n);
    FOR(i,1,n)scanf("%d",&t[i]);
    FOR(i,1,n)if(t[i]>mx)mx=t[p=i];
    FOR(i,1,n-p)a[i]=t[i+p];
    FOR(i,n-p+1,n-1)a[i]=t[i-n+p];
    n--;
    a[0]=2e9,stk[top=0]=0;
    FOR(i,1,n)
    {
        while(a[i]>=a[stk[top]])
        {
            if(a[i]==a[stk[top]])s[i]=s[stk[top]]+1;
            top--;
        }
        if(top)ans++;
        stk[++top]=i;
    }
    a[n+1]=2e9,stk[top=0]=n+1;
    DOR(i,n,1)
    {
        while(a[i]>=a[stk[top]])top--;
        if(top)ans++;
        stk[++top]=i;
    }
    FOR(i,1,n)ans+=s[i];
    h=-1;
    FOR(i,1,n)
    {
        if(a[i]>=h)
        {
            h=a[i];
            if(!mark[i])
            {
                mark[i]=1;
                ans++;
            }
        }
    }
    h=-1;
    DOR(i,n,1)
    {
        if(a[i]>=h)
        {
            h=a[i];
            if(!mark[i])
            {
                mark[i]=1;
                ans++;
            }
        }
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Paulliant/article/details/81041978