# 题目大意你现在有一个空字符串 t t t ,给定一个字符串 s s s ,你可以对 t t t 做任意顺序的任意次的以下两种操作
在 t t t 的末尾加一个 0
循环移位一位(往后移 +1
) 请问,最少要多少步能使得 t t t 变得和 s s s 一样?
# 数据范围1 ≤ ∣ S ∣ ≤ 5 × 1 0 5 1 \le |S| \le 5 \times 10^5 1 ≤ ∣ S ∣ ≤ 5 × 1 0 5 # 题解数据范围到了 5 × 1 0 5 5 \times 10^5 5 × 1 0 5 ,肯定就不能暴力了 如果 S i > S i − 1 S_i > S_{i-1} S i > S i − 1 ,那只能 S i − 1 S_{i-1} S i − 1 移动一整圈,在移动的过程中,把 S i S_i S i 添进去 其余情况,在之前字符加的过程中,添加即可
代码其实很简单,我一开始想的是,连续升序只要加一个 10
,连续降序加一个 s[i] - '0'
,但是这样就没有注意到,连续升序的话,必须是一个一个来,不能一次性把所有连续升序的都处理掉,因为如果都处理掉的话,那么同加的时候,添加的 S i S_i S i 是必然大于后续添加的 S i + 1 , i + 2.... S_{i+1, i + 2....} S i + 1 , i + 2 . . . . 的
C++ 1 2 3 4 5 6 7 8 9 void solve () { cin >> s, res = s.size () + s[0 ] - '0' ; for (int i = 1 ; i < s.size (); i++) if (s[i] > s[i - 1 ]) res += 10 ; cout << res << '\n' ; }
# 题目大意有一个 H H H 行 W W W 列的网格,每个格子上有一个非负整数 A i , j A_{i,j} A i , j 现在放置若干块(可以不放)多米诺骨牌,一张牌覆盖两个相邻的单元格,任意一个单元格不能被多张牌覆盖 对于一个摆放,其摆放分数定义为:未被任何骨牌覆盖的单元格中的所有整数的异或值 问,对于一个给定的图,其得分最大值为?
# 数据范围# 题解观察到,数据范围其实很小,考虑爆搜 暴力遍历每个格子,以当前格子为多米诺骨牌的左上角块,暴力搜索放 / 不放 这里有个记录状态的小技巧,当数据范围小的时候,可以把整个矩阵的搜索情况 s t st s t 矩阵处理成字符串记录进 s e t set s e t
c++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 int n, m, ans = 0 ;vector<vector<bool >> st (21 , vector <bool >(21 , 0 )); vector<vector<int >> a (21 , vector <int >(21 )); set<string> ts; void dfs () { string tmp = "" ; for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= m; j++) tmp += to_string (st[i][j]); if (ts.find (tmp) != ts.end ()) return ; ts.insert (tmp); for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= m; j++) { if (j < m && (!st[i][j] && !st[i][j + 1 ])) { st[i][j] = st[i][j + 1 ] = true ; dfs (); st[i][j] = st[i][j + 1 ] = false ; } if (i < n && (!st[i][j] && !st[i + 1 ][j])) { st[i][j] = st[i + 1 ][j] = true ; dfs (); st[i][j] = st[i + 1 ][j] = false ; } } int t = 0 ; for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= m; j++) if (!st[i][j]) t ^= a[i][j]; ans = max (ans, t); } void solve () { cin >> n >> m; for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= m; j++) cin >> a[i][j]; dfs (); cout << ans << '\n' ; }
# 题目大意给你一个长度为 2 N 2N 2 N 的括号数列 A A A ,定义其得分
对于所有 s[i] == ')'
,A i ← 0 A_i \leftarrow 0 A i ← 0 得分为上述操作后,A A A 中所有元素的和 A A A 是数列,把你操作后变为 0 0 0 的看作 )
,没操作的看作 (
,操作后,s s s 需要是一个合法的括号序列
请求出一个合法的括号序列 s s s ,它得分的最大值
# 数据范围1 ≤ T ≤ 500 1 \le T \le 500 1 ≤ T ≤ 5 0 0 1 ≤ N ≤ 2 × 1 0 5 1 \le N \le 2 \times 10^5 1 ≤ N ≤ 2 × 1 0 5 # 题解题目是把 )
直接变为 0 0 0 如果按一半左括号,一半右括号来算的话,那损失可能会达到最大,不妨把初始括号序列想成 ()()()...()()()
那么在遍历的时候,当找到 (
时,可以观察其是否左端存在,值更大的 )
,如果存在的话,可以替换掉当前这个 (
而这个找更大值的步骤,可以用优先队列 实现
在写这类题目的时候,均可以用形如 ()()()()...()()()
的括号序列去遍历替换,因为一个左括号和其左侧的任意一个右括号交换的话,其结果都会是一个合法的括号序列,而维护值可以用优先队列,整个思想复杂度是 O ( n ⋅ l o g ( n ) ) O(n·log(n)) O ( n ⋅ l o g ( n ) ) 的、
c++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void solve () { cin >> n, a = vector <int >(2 * n + 1 ); int res = 0 ; priority_queue<int , vector<int >, less<int >> heap; for (int i = 1 ; i <= 2 * n; i++) { cin >> a[i]; if (i & 1 ) { if (!heap.empty () && heap.top () > a[i]) res += heap.top (), heap.pop (), heap.push (a[i]); else res += a[i]; } else heap.push (a[i]); } cout << res << '\n' ; }
# 题目大意给定一个长度为 N N N 的数列 A = ( a 1 , . . . , a N ) A=(a_1,...,a_N) A = ( a 1 , . . . , a N ) ,请针对所有 1 ≤ k ≤ N 1 \le k \le N 1 ≤ k ≤ N ,计算
A A A 数列中,长为 k k k 的 N − k + 1 N-k+1 N − k + 1 个连续子序列的最大值的和# 数据范围1 ≤ N ≤ 2 × 1 0 5 1 \le N \le 2 \times 10^5 1 ≤ N ≤ 2 × 1 0 5 0 ≤ A i ≤ 1 0 7 0 \le A_i \le 10^7 0 ≤ A i ≤ 1 0 7 # 题解# 错解 - ST 表时间复杂度直接爆炸了,A B C ABC A B C 没给 A C AC A C 一个点
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <bits/stdc++.h> #define int long long using namespace std;typedef pair<int , int > PII;const int N = 2e5 + 10 ;int dx[] = {0 , 0 , 0 , 1 , -1 , 1 , 1 , -1 , -1 };int dy[] = {0 , 1 , -1 , 0 , 0 , 1 , -1 , 1 , -1 };int n;vector<int > a; int f[N][30 ], preN[N];void solve () { cin >> n, a = vector <int >(n + 1 ); for (int i = 1 ; i <= n; i++) cin >> a[i], f[i][0 ] = a[i]; for (int j = 1 ; j < 30 ; j++) for (int i = 1 ; i + (1 << j) - 1 <= n; i++) f[i][j] = max (f[i][j - 1 ], f[i + (1 << (j - 1 ))][j - 1 ]); for (int k = 1 ; k <= n; k++) { int res = 0 ; for (int i = 1 ; i <= n - k + 1 ; i++) { int l = i, r = i + k - 1 , s = preN[r - l + 1 ]; res += max (f[l][s], f[r - (1 << s) + 1 ][s]); } cout << res << '\n' ; } } signed main () { ios::sync_with_stdio (false ); cin.tie (0 ), cout.tie (0 ); preN[1 ] = 0 , preN[2 ] = 1 ;; for (int i = 3 ; i < N; i++) preN[i] = preN[i / 2 ] + 1 ; int _ = 1 ; while (_--) solve (); return 0 ; }
# 正解 - 单调栈 + 差分利用单调栈,得到每一个 a i a_i a i 能作用的区间 [ l i , r i ] [l_i, r_i] [ l i , r i ] ,a i = max i = l i r i a i a_i=\max\limits_{i=l_i}^{r_i}a_i a i = i = l i max r i a i
最小的窗口大小是当子数组包含 i i i 并且往两头尽量缩小m i n ( i − l i , r i − i ) + 1 min(i - l_i, r_i-i)+1 m i n ( i − l i , r i − i ) + 1 最大的窗口大小是当子数组包含整个 [ l i , r i ] [l_i,r_i] [ l i , r i ] r i − l i + 1 r_i-l_i+1 r i − l i + 1 对于 a i a_i a i 及其作用区间 [ l i , r i ] [l_i,r_i] [ l i , r i ] 有 记 l e n = r i − l i + 1 , L = i − l i + 1 , R = r i − i + 1 m i n n = m i n ( L , R ) , m a x x = m a x ( L , R ) len = r_i - l_i + 1,~L=i-l_i+1,~R=r_i-i+1~minn=min(L,R),~maxx=max(L,R) l e n = r i − l i + 1 , L = i − l i + 1 , R = r i − i + 1 m i n n = m i n ( L , R ) , m a x x = m a x ( L , R )
a i a_i a i 可贡献给 k = 1 → k = l e n k=1 \to k = len k = 1 → k = l e n 的区间
k = 1 → k = m i n n k = 1 \to k = minn k = 1 → k = m i n n 时k k k 区间中任意一点,均能当作 a i a_i a i ,所以答案就是 k k k k = m i n n → k = m a x x k=minn \to k =maxx k = m i n n → k = m a x x 时k k k 区间中,只有 1 → m i n n 1 \to minn 1 → m i n n 能当作 a i a_i a i ,所以答案是 m i n n minn m i n n k = m a x x → l e n k=maxx \to len k = m a x x → l e n 时能贡献的越来越少,直到 k = l e n k=len k = l e n ,只有一个贡献 贡献会像一个梯形,先单增,再不变,最后单减 用两次前缀和实现二阶差分即可(第一阶差分 → \to → 斜率,第二阶差分 → \to → 值)C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 void solve () { cin >> n; vector<int > a (n + 2 ) , l (n + 1 ) , r (n + 1 ) , ans (n + 3 ) ; stack<int > cl, cr; a[0 ] = a[n + 1 ] = 1e18 ; for (int i = 1 ; i <= n; i++) cin >> a[i]; cl.push (0 ); for (int i = 1 ; i <= n; i++) { while (a[i] > a[cl.top ()]) cl.pop (); l[i] = cl.top () + 1 , cl.push (i); } cr.push (n + 1 ); for (int i = n; i >= 1 ; i--) { while (a[i] >= a[cr.top ()]) cr.pop (); r[i] = cr.top () - 1 , cr.push (i); } for (int i = 1 ; i <= n; i++) { int len = r[i] - l[i] + 1 , minn = min (i - l[i], r[i] - i) + 1 , maxx = max (i - l[i], r[i] - i) + 1 ; ans[1 ] += a[i], ans[minn + 1 ] -= a[i], ans[maxx + 1 ] -= a[i], ans[len + 2 ] += a[i]; } for (int i = 1 ; i <= n; i++) ans[i] += ans[i - 1 ]; for (int i = 1 ; i <= n; i++) ans[i] += ans[i - 1 ], cout << ans[i] << '\n' ; }