CodeForces5E 环转链,dp思想

http://codeforces.com/problemset/problem/5/E

众所周知,在很久以前,在今天的 Berland 地区,居住着 Bindian 部落。他们的首都被 n 座山所环绕,形成了一个圆形。在每座山上,有一名看守人,昼夜看守着相邻的山。

万一出现了任何危险,看守人可以在山上点燃烽火。如果连接两座山的圆弧上,没有任何山高于这两座山中的任何一座,那么这两座山的一个看守人可以看见另一个看守人的信号。对于任意两座山,由于存在两条不同的圆弧连接着它们,如果在至少一条圆弧上满足前述条件,信号就能被看见。例如,对于任意两个相邻的看守人,一方的信号将能够被另一方看见。

这个守护系统的一个重要特性,就是能够相互看见对方信号的看守人“成对”的数目。请根据给定各山的高度,求出这样的“成对”数目。

输入
输入数据的第一行,包含了一个整数 n (3 ≤ n ≤ 106), n — 环绕首都的山的数量。第二行包含了 n 个数字 — 按顺时针顺序的各山的高度。所有的高度数字为整数,介于 1109 之间。

输出
打印所要求的“成对”数目。
题意

首先遇到的就是对环的操作,很显然直接在环上操作是不那么容易的,开始想到对整个链进行延长两倍的操作进行处理,但是这样会很麻烦,难以下手

由于这题的特殊特性,当我们尝试把最高的山脉放在链的最左边时,整个环就变成了一个相同题意下的链,可以方便操作很多。

一个小技巧在寻找最大值的时候用一个变量存储最大值下标可以很便捷

然后遇到的问题就是求对数,开始想到了单调队列去做,但是因为会有类似 1 1 2 2这样的样例,单调队列很难维护

只能考虑用其他的方法

我们假设每一个山脉都是对数里面较低的山脉,我们对每个山脉计算的就只有两边比他大的山脉。

由此得到维护一个left数组,right数组表示这个山脉两边比他高的山脉,如果存在就ans++

但是还有一个问题就是1 1 1 1 这个样例,所有等高山脉是可以互相看见的。我们还需要计算l 到 r 之间和这个山脉高度相等的山脉加上去。

加两边会导致重复计算,如果每一个山脉都只加右边,就可以完美规避这个问题,也就是说用一个same数组来存储这个i 到 right[i]之间有多少和他高度相同的山脉

求left right same的方法当然不是暴力,用dp的思想可以在线性的时间内解决这个问题

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)  
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))  
#define Sca(x) scanf("%d", &x)
#define Scl(x) scanf("%lld",&x);  
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);  
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long  
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second 
typedef vector<int> VI;
const double eps = 1e-9;
const int maxn = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7; 
int N,M,tmp,K;
int a[maxn];
int b[maxn];
int Left[maxn],Right[maxn],same[maxn];
int main()
{
    Sca(N);
    For(i,1,N) Sca(a[i]);
    int MAX = 1;
    For(i,2,N){
        if(a[i] > a[MAX]) MAX = i;
    }
    For(i,1,MAX - 1){
        b[N - MAX + 1 + i] = a[i];
    }
    For(i,MAX,N){
        b[i - MAX + 1] = a[i];
    }
    int cnt = 0;
    Left[1] = 1;
    For(i,2,N){
        Left[i] = i - 1;
        while(Left[i] > 1 && b[Left[i]] <= b[i]){
            Left[i] = Left[Left[i]];
        }
    }
    _For(i,N,1){
        Right[i] = i + 1;
        while(Right[i] <= N && b[Right[i]] < b[i]){
            Right[i] = Right[Right[i]];
        }
        if(Right[i] <= N && b[Right[i]] == b[i]){
            same[i] = same[Right[i]] + 1;
            Right[i] = Right[Right[i]];
        }
    }
    LL ans = 0;
    For(i,2,N){
        ans += same[i] + 2;
        if(Left[i] == 1 && Right[i] == N + 1){
            ans--;
        }
    }
    Prl(ans);
    #ifdef VSCode
    system("pause");
    #endif
    return 0;
}

 

posted @ 2018-09-06 23:52  Hugh_Locke  阅读(179)  评论(0编辑  收藏  举报