Codeforces Round 1047 (Div. 3) F题题解记录
大致题意:令F(x,y)F(x,y)F(x,y):对于任意与xxx的前缀最大值相同的数组zzz,满足zi=yiz_i=y_izi=yi中iii的个数。x,y,zx,y,zx,y,z数组大小均相同。给定xxx和yyy,求∑i=1n∑j=inF(subarrayx[i,j],subarrayy[i,j])\sum_{i=1}^{n}\sum_{j=i}^{n}F(subarray_x[i,j],subarray_y[i,j])∑i=1n∑j=inF(subarrayx[i,j],subarrayy[i,j])。
解:
考虑贡献。对于xi=yix_i=y_ixi=yi,包含iii位置的子数组都会贡献+1+1+1。共有i×(n−i+1)i \times (n-i+1)i×(n−i+1)。否则:首先由于前缀最大值的要求,对于xxx的子数组,我们随便考虑一个2,4,1,32,4,1,32,4,1,3,首先i=1i=1i=1的时候,ziz_izi必须取222;i=2i=2i=2的时候,z2z_2z2必须取444;但是对于i=3,4i=3,4i=3,4,ziz_izi均可取小于等于444的任何数,因为此时前缀最大值都相同。也就是说,对于iii,其左边第一个大于等于他的数的位置ppp,左端点从111到ppp这段区间内,ziz_izi都可以取[1,xp][1,x_p][1,xp]。如果此时bi∈[1,xp]b_i\in[1,x_p]bi∈[1,xp],那么就没问题,ziz_izi完全可以变成yiy_iyi;但是如果大于xpx_pxp,那么此时ppp的范围就需要缩小了。我们需要将ppp的位置再往前推,找到第一个大于等于yiy_iyi的数的位置,因为这个位置下ziz_izi的范围可以囊括yiy_iyi。故针对每个位置iii,我们需要记录:离iii最近的大于等于aia_iai的数的位置以及大于等于bib_ibi的数的位置。前者可以通过单调栈实现,后者可以用单调栈+二分实现。
void solve() {int n;cin >> n;vector<int> a(n + 1), b(n + 1);for (int i = 1; i <= n; i++)cin >> a[i];for (int i = 1; i <= n; i++)cin >> b[i];int ans = 0;for (int i = 1; i <= n; i++) {if (a[i] == b[i])ans += (i) * (n - i + 1);}deque<int> q;vector<int> idx1(n + 1);for (int i = 1; i <= n; i++) {if (q.empty())q.push_back(i);else {while (!q.empty() && a[i] > a[q.back()]) q.pop_back();if (!q.empty()) {idx1[i] = q.back();}q.push_back(i);}}vector<int> pp(n + 1);int sz = 0;for (int i = 1; i <= n; i++) {if (sz == 0)pp[++sz] = i;else {while (sz && a[i] > a[pp[sz]])sz--;if (sz) {int l = 1, r = sz;int as = -1;while (l <= r) {int mid = (l + r + 1) >> 1;if (a[pp[mid]] >= b[i]) {as = max(as, mid);l = mid + 1;} else {r = mid - 1;}}if (as != -1 && a[i] != b[i]) {ans += min(pp[as], idx1[i]) * (n - i + 1);}}pp[++sz] = i;}}cout << ans << endl;
}
idx1idx1idx1为前者,pppppp为后者。另外需要注意:如果aia_iai等于bib_ibi,不要重复计算贡献。