CODEFORCES #526 F. Pudding Monsters (排列模型的转化与分治的应用)
题目描述:
给出一个$N*N$的矩阵,每一行、每一列,有且仅有一个特殊点。问有多少个$K*K$的矩阵内恰好有$K$个特殊点。
解题思路:
转换下模型,矩阵可以看成一个$N$的排列,求的是有多少连续子序列中的数是一个区间中连续的,也就是最大数减最小数等于长度减一。那么我们就可以考虑分治解决,对于跨过分治点$m$的情况的,可以分两大类考虑,最值各在分治点一侧、同在分治点一侧。第一种,可以直接枚举一侧长度,就能计算出另一侧长度,在判断是否合法。第二种,假如$i$在左,$j$在右,一种情况是$max[j]-min[i]=i-j$,那么可以变成$max[j]+j=min[i]+i$,因为$max$和$min$都是单调的,所以可以用单调队列和桶维护。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define i64 long long 5 using namespace std; 6 7 const int N = 5e4 + 10; 8 int n, s[N], mn[N], mx[N], sum[N * 2]; 9 i64 ans; 10 11 void divide(int l, int r) { 12 if (l == r) return; 13 int mid = l + r >> 1; 14 divide(l, mid); 15 divide(mid + 1, r); 16 mn[mid] = mx[mid] = s[mid]; 17 mn[mid + 1] = mx[mid + 1] = s[mid + 1]; 18 for (int i = mid - 1; i >= l; i --) mn[i] = min(s[i], mn[i + 1]), mx[i] = max(s[i], mx[i + 1]); 19 for (int i = mid + 2; i <= r; i ++) mn[i] = min(s[i], mn[i - 1]), mx[i] = max(s[i], mx[i - 1]); 20 for (int i = mid; i >= l; i --) { 21 int j = mx[i] - mn[i] + i; 22 if (j > mid && j <= r && mx[j] < mx[i] && mn[j] > mn[i]) ans ++; 23 } 24 for (int j = mid + 1; j <= r; j ++) { 25 int i = j - mx[j] + mn[j]; 26 if (i >= l && i <= mid && mx[j] > mx[i] && mn[j] < mn[i]) ans ++; 27 } 28 int hea, tai; 29 hea = tai = mid + 1; 30 for (int i = mid; i >= l; i --) { 31 while (tai <= r && mn[tai] > mn[i]) ++ sum[mx[tai] - tai + n], ++ tai; 32 while (hea < tai && mx[hea] < mx[i]) -- sum[mx[hea] - hea + n], ++ hea; 33 ans += (i64)sum[mn[i] - i + n]; 34 } 35 while (hea < tai) -- sum[mx[hea] - hea + n], ++ hea; 36 hea = tai = mid; 37 for (int j = mid + 1; j <= r; j ++) { 38 while (hea >= l && mn[hea] > mn[j]) ++ sum[mx[hea] + hea], -- hea; 39 while (tai > hea && mx[tai] < mx[j]) -- sum[mx[tai] + tai], -- tai; 40 ans += (i64)sum[mn[j] + j]; 41 } 42 while (tai > hea) -- sum[mx[tai] + tai], -- tai; 43 } 44 45 int main() { 46 ans = 3e10; 47 scanf("%d", &n); 48 for (int i = 1; i <= n; i ++) { 49 int x, y; 50 scanf("%d %d", &x, &y); 51 s[x] = y; 52 } 53 divide(1, n); 54 printf("%lld", ans + (i64)n); 55 return 0; 56 }