cf5 E. Bindian Signalizing
题意:
给定长为 \(n\) 的环,对环上两点 \(i,j\),若存在 \(i\) 到 \(j\) 的弧,弧中的数均不大于 \(a_i\) 或 \(a_j\),则称 \(i\) 和 \(j\) 可以互相看到。求可以互相看到的位置对数。
思路:
拆环成链,把最大的数 \(mx\) 放在链头(可能不止一个最大的,但没关系)
在链尾也插一个 \(mx\)
对环中的位置 \(i\),求左边最近的大于 \(a_i\) 的位置 \(left_i\) 和右边最近的大于 \(a_i\) 的位置 \(right_i\),则有两对 \(<left_i,i>\) 和 \(<i,right_i>\)。另外 \((left_i,right_i)\) 中所有与 \(i\) 相等的位置 \(p\) 也会与 \(i\) 配对,为了避免重复计数我们统计 \((left_i,i-1)\) 中 \(p\) 的数量,记为 \(same_i\)
void sol() {
int n; cin >> n;
vector<int> a(n); for(int &x : a) cin >> x;
rotate(a.begin(), max_element(a.begin(),a.end()), a.end());
a.push_back(a[0]);
vector<int> lef(n), rig(n), same(n);
stack<int> stk;
for(int i = 0; i < n; i++) {
while(stk.size() && a[stk.top()] <= a[i]) {
if(a[stk.top()] == a[i]) same[i] = same[stk.top()] + 1;
stk.pop();
}
lef[i] = stk.size() ? stk.top() : -1;
stk.push(i);
}
stack<int>().swap(stk); //清空
stk.push(n);
for(int i = n-1; i >= 0; i--) {
while(stk.size() && a[stk.top()] <= a[i]) stk.pop();
rig[i] = stk.size() ? stk.top() : -1;
stk.push(i);
}
ll ans = 0; for(int i = 1; i < n; i++)
ans += (lef[i] != -1) + (rig[i] != -1)
- (lef[i] == 0 && rig[i] == n) + same[i]; //注意0与n是同一个位置
cout << ans << '\n';
}

浙公网安备 33010602011771号