5E - Bindian Signalizing 环转链,dp思想
作者:互联网
原题链接https://codeforces.com/problemset/problem/5/E
这题对我这个菜鸡来说,实在是太难了,捣鼓了半天才看懂大佬的代码。
题意:给你一个环,环上每个点都有一个权值\(h_i\)让你求出有多少数对\((i, j)\),存在一条从\(i\)到\(j\)的弧满足弧上的所有数都小于等于端点\(h_i和h_j\).
思路:首先遇到的就是对环的操作,很显然直接在环上操作是不那么容易的,开始想到对整个链进行延长两倍的操作进行处理,但是这样会很麻烦,难以下手
由于这题的特殊特性,当我们尝试把最高的山脉放在链的最左边时,整个环就变成了一个相同题意下的链,可以方便操作很多。
一个小技巧在寻找最大值的时候用一个变量存储最大值下标可以很便捷
然后遇到的问题就是求对数,开始想到了单调队列去做,但是因为会有类似 1 1 2 2这样的样例,单调队列很难维护
只能考虑用其他的方法
我们假设每一个山脉都是对数里面较低的山脉,我们对每个山脉计算的就只有两边比他大的山脉。
由此得到维护一个left数组,right数组表示这个山脉两边比他高的山脉,如果存在就ans++
但是还有一个问题就是1 1 1 1 这个样例,所有等高山脉是可以互相看见的。我们还需要计算l 到 r 之间和这个山脉高度相等的山脉加上去。
加两边会导致重复计算,如果每一个山脉都只加右边,就可以完美规避这个问题,也就是说用一个same数组来存储这个i 到 right[i]之间有多少和他高度相同的山脉
求left right same的方法当然不是暴力,用dp的思想可以在线性的时间内解决这个问题
代码如下
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
#include<iomanip>
#define IOS cin.tie(0), ios::sync_with_stdio(false)
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e6 + 10, M = 1e5 + 10, mod = 1e9 + 7;
const double eps = 1e-4;
const double pi = acos(-1);
int a[N], b[N];
int l[N], r[N], same[N];
// l[i]表示 i 左边第一个比 i 大的数的位置
// r[i]表示 i 右边第一个比 i 大的数的位置
// same[i] 表示在 i ~ r[i] 这个区间中和 i 相同的数的个数
// 只算右边的相同的数 在记数时不会重复
int main()
{
int n;
scanf("%d", &n);
for(int i = 1 ; i <= n ; ++ i) scanf("%d", &a[i]);
int d = 1;
for(int i = 2 ; i <= n ; ++ i) //找到最高的山 去环成链
if(a[i] > a[d]) d = i;
for(int i = 1 ; i < d ; ++ i) //把最高的山放在链首
b[n - d + 1 + i] = a[i];
for(int i = d ; i <= n ; ++ i)
b[i - d + 1] = a[i];
b[n + 1] = a[d];
l[1] = 1;
for(int i = 2 ; i <= n ; ++ i) //搞出 l 数组
{
l[i] = i - 1;
//前面的判断是为了防止有多个最高的而 TLE
while(l[i] > 1 && b[l[i]] <= b[i])
l[i] = l[l[i]];
}
for(int i = n ; i >= 1 ; -- i) //搞出 r 和 same 数组
{
r[i] = i + 1; //同理搞r数组
while(r[i] <= n && b[r[i]] < b[i])
r[i] = r[r[i]];
// 类似搞出same数组
if(r[i] <= n && b[r[i]] == b[i])
{
same[i] = same[r[i]] + 1;
r[i] = r[r[i]];
}
}
ll res = 0;
for(int i = 2 ; i <= n ; ++ i)
{ // 一个1是 b[l[i]] ~ b[i] 另一个是 b[i] ~ b[r[i]]
res += same[i] + 1 + 1;
if(l[i] == 1 && r[i] == n + 1) //当 b[l[i]] 和 b[r[i]]重复时
res --;
}
printf("%lld\n", res);
return 0;
}
标签:right,Bindian,same,Signalizing,int,数组,山脉,include,环转链 来源: https://www.cnblogs.com/luoyicong/p/14615546.html