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 }
时间: 2024-08-06 11:54:08

CODEFORCES #526 F. Pudding Monsters的相关文章

Codeforces 727 F. Polycarp&#39;s problems

Description 有一个长度为 \(n\) 有正负权值的序列,你一开始有一个值,每次到一个权值就加上,最少需要删掉多少数值才能到序列末尾.\(n \leqslant 750,m \leqslant 2 \times 10^5\) Sol DP+二分. 发现这个东西有后效性,就是前面选不选会影响后面的决策,并且权值太大无法记录. 但是我们可以倒着做,因为后面的决策无法影响前面的决策. \(f[i][j]\) 表示到 \(i\) 删掉 \(j\) 个至少需要的初始权值. 因为初始权值非负,所以

codeforces 526 e Transmitting Levels

codeforces 526 e Transmitting Levels 题意: 给出n个数a1,a2,...,an,这n个数首尾相接形成一个环. 如:1 2 3 (1,2) (2,3) (3,1) 相连 现在再给出q个询问,每个询问为一个b,求把这n个数分成相连的m段,使得每段的和不超过b,求m的最小值. 限制: 2 <= n <= 1e6 1 <= q <= 50 1 <= ai <= 1e9 max(ai) <= b <= 1e15 思路: 先找出一个

codeforces 526 d Om Nom and Necklace next数组的灵活运用

codeforces 526 d Om Nom and Necklace 题意: 给出一个字符串,问对于字符串的每个位置p,求从0到p的字符串是否符合格式:S=A+B+A+B+A+...+A+B+A,其中A,B是字符串,且可以是空串. 限制: 字符串长度1e6 思路: next数组的灵活运用. /*codeforces 526 d Om Nom and Necklace 题意: 给出一个字符串,问对于字符串的每个位置p,求从0到p的字符串是否符合格式:S=A+B+A+B+A+...+A+B+A,

Codeforces 835 F. Roads in the Kingdom

\(>Codeforces\space835 F. Roads in the Kingdom<\) 题目大意 : 给你一棵 \(n\) 个点构成的树基环树,你需要删掉一条环边,使其变成一颗树,并最小化删掉环边后的树的直径. \(n \leq 2 \times 10^5\) 树的边权 $ \leq 10^9 $ 解题思路 : 考虑最终树的直径可能由两部分组成,答案是其中的最大值 第一种就是外向树内的直径的最大值,这个只需要随便\(dp\)一下即可,这里不过多讨论 第二种情况树的直径经过原来的环,

Codeforces 1166 F. Vicky&#39;s Delivery Service 并查集+set

题意:有n个点,m条边,边有c种颜色,q次操作. 每个边都有一种颜色. 然后操作有两种,一种是再加一条边,另一种是查询能否从x达到y. 移动的限制是,连着走两步必须是同一种颜色,如果走奇数步,最后一步可以是任意颜色. 例子:1-2-3-4-5-6. 这个题颜色种类很多,我是用map<int,vector<int>>来存边. 我们首先可以想到 对于点x同种颜色连着的点都是可以相互移动的,所以我们可以用这种方法将它们用并查集合并,所以我们也可以直接map<int,int>来

# codeforces 1272 F. Two Bracket Sequences(三维dp + bfs)

codeforces 1272 F. Two Bracket Sequences(三维dp + bfs) 题目大意 输入两个括号序列 s,t(不一定合法),你需要构造一个尽可能短的合法括号序列使得s,t 都是这个序列的子序列(子序列意味着不用连续) 解题思路 dp[i][j][k]表示匹配到s的第i个字符,匹配到t的第j个字符,并且此时(的个数比)多k个的时候的最小合法序列长度,k的上限是200(s和t中最多200个(或者)). 状态转移: 枚举答案合法序列的每一位是放置(或者) ? 放置(,如

Codeforces 526F Pudding Monsters

先把题目抽象一下: 有一个静态的数组,求有多少个区间[i,j]满足:j-i==max{ai,...,aj}-min{ai,...,aj} 也就是要求max-min+i-j==0的区间数 所以肿么做呢? 首先枚举i(这里倒着做,比较好理解),维护以i为开头的所有区间 相当于每次要在一坨区间的最前面同时加一个元素(并且增加一个仅含有ai的区间[i,i]) 然后很惊喜的发现实际上对于这一坨区间的权值(max-min+i-j)只有以下几个操作: 1.同时-1,因为i减小了1 2.改max(这个一定只有有

Codeforces 589F F. Gourmet and Banquet(二分+贪心)

题目地址:http://codeforces.com/problemset/problem/589/F 思路:先贪心按照右端点值排序(先把对后面影响最小的菜吃掉),二分吃每道菜的时间即可. #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn=105; const int maxt=1e5+50

codeforces 825F F. String Compression dp+kmp找字符串的最小循环节

/** 题目:F. String Compression 链接:http://codeforces.com/problemset/problem/825/F 题意:压缩字符串后求最小长度. 思路: dp[i]表示前i个字符需要的最小次数. dp[i] = min(dp[j]+w(j+1,i)); (0<=j<i); [j+1,i]如果存在循环节(自身不算),那么取最小的循环节x.w = digit((i-j)/x)+x; 否则w = i-j+1; 求一个区间最小循环节: 证明:http://w