Link
[USACO10MAR] Great Cow Gathering G
换根 dp 板子
先用树形 dp 求出以 1 1 1 为根的最小不方便值,然后 bfs 向儿子换根
f v = f u + ( s i z u − s i z v × 2 ) × w f_v=f_u + (siz_u - siz_v \times 2) \times w f v = f u + ( s i z u − s i z v × 2 ) × w
这个手推一下就好了,大概就是把两边子树中的点经过 ( u , v ) (u,v) ( u , v ) 这条边的代价算一下。
Code
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 #include <bits/stdc++.h> #define ll long long using namespace std;const int N = 1e5 + 5 ;int n, c[N];struct edge { int v, w, nxt; }e[N << 1 ]; int head[N], tot;void add (int u, int v, int w) { e[++tot] = (edge) {v, w, head[u]}; head[u] = tot; } ll f[N], siz[N], ans; void dfs (int u, int fa) { siz[u] = c[u]; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].v; if (v == fa) continue ; dfs (v, u); siz[u] += siz[v]; f[u] += f[v] + siz[v] * e[i].w; } return ; } queue <int > que; bool vis[N];void bfs () { ans = 1e18 ; que.push (1 ); while (!que.empty ()) { int u = que.front (); que.pop (); if (vis[u]) continue ; vis[u] = 1 ; ans = min (ans, f[u]); for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].v; f[v] = f[u] + (siz[u] - siz[v] * 2 ) * e[i].w; siz[v] = siz[u]; que.push (v); } } return ; } int main () { scanf ("%d" , &n); for (int i = 1 ; i <= n; i++) scanf ("%d" , &c[i]); for (int i = 1 , u, v, w; i < n; i++) { scanf ("%d%d%d" , &u, &v, &w); add (u, v, w), add (v, u, w); } dfs (1 , 0 ); bfs (); printf ("%lld\n" , ans); return 0 ; }
[USACO09FEB] Stock Market G
CSP-J2019 T3 原题
感觉思路还是比较难的,虽然代码难度极低
要求最终能获得的最大钱数,其实可以分别对每一天,求当天能获得的最大钱数。
对于每个物品在每天,有三种情况:
不买
今天买,明天卖
今天买,过几天卖
其实第三种可以通过第二种实现
今天买了,明天卖掉,然后明天再买,后天卖掉…
就相当于今天买了,过几天再卖。
所以就两种情况,然后就是一个完全背包了
设 f i f_i f i 表示手上有 i i i 元钱时最大收益,注意总钱数要每天更新。
Code
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 #include <bits/stdc++.h> using namespace std;const int MAXN = 5e5 + 5 ;int n, m, s, p[55 ][15 ];int f[MAXN];int main () { scanf ("%d%d%d" , &n, &m, &s); for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= m; j++) scanf ("%d" , &p[i][j]); for (int j = 2 ; j <= m; j++) { int mx = 0 ; memset (f, 0 , sizeof (f)); for (int i = 1 ; i <= n; i++) { for (int k = p[i][j - 1 ]; k <= s; k++) { f[k] = max (f[k], f[k - p[i][j - 1 ]] + p[i][j] - p[i][j - 1 ]); mx = max (mx, f[k]); } } s += mx; } printf ("%d\n" , s); return 0 ; }
USACO13MAR Necklace G
这里用的 AC自动机,也可以用 kmp,但如果有多个不能出现的子串就不行了。
正难则反,求出最多能保留多少,然后减一下就行了
先建出 AC自动机,在上面 dp
设 f i , j f_{i,j} f i , j 表示在第 i i i 个位置,在 AC自动机上走到了 j j j 时最多能保留多长
如果 j j j 不是子串的末尾,f i , j = m a x ( f i , j , f i − 1 , j ) f_{i,j}=max(f_{i,j},f_{i-1,j}) f i , j = m a x ( f i , j , f i − 1 , j )
令 k = t r i e j , a i k=trie_{j,a_i} k = t r i e j , a i ,也就是在 AC自动机上从 j j j 向后沿着 a i a_i a i 走到哪个点。
如果 k k k 不是子串的末尾,f i , k = m a x ( f i , k , f i − 1 , j + 1 ) f_{i,k}=max(f_{i,k},f_{i-1,j}+1) f i , k = m a x ( f i , k , f i − 1 , j + 1 )
Code
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 53 54 55 56 57 58 #include <bits/stdc++.h> using namespace std;const int N = 1e4 + 5 ;char a[N], b[N];int n, m, nxt[N];int trie[N][30 ], tot, ed;int fail[N];int f[N][1010 ];void insert (char s[]) { int len = strlen (s + 1 ), x = 0 ; for (int i = 1 ; i <= len; i++) { int c = s[i] - 'a' ; if (!trie[x][c]) trie[x][c] = ++tot; x = trie[x][c]; } ed = x; return ; } void build () { queue <int > que; for (int i = 0 ; i < 26 ; i++) if (trie[0 ][i]) que.push (trie[0 ][i]); while (!que.empty ()) { int x = que.front (); que.pop (); for (int i = 0 ; i < 26 ; i++) if (trie[x][i]) fail[trie[x][i]] = trie[fail[x]][i], que.push (trie[x][i]); else trie[x][i] = trie[fail[x]][i]; } return ; } int main () { scanf ("%s%s" , a + 1 , b + 1 ); n = strlen (a + 1 ), m = strlen (b + 1 ); insert (b), build (); for (int i = 1 ; i <= n; i++) for (int j = 0 ; j <= m; j++) { if (j != ed) f[i][j] = max (f[i][j], f[i - 1 ][j]); int k = trie[j][a[i] - 'a' ]; if (k != ed) f[i][k] = max (f[i][k], f[i - 1 ][j] + 1 ); } int ans = 0 ; for (int i = 0 ; i <= m; i++) ans = max (ans, f[n][i]); printf ("%d\n" , n - ans); return 0 ; }
USACO12DEC Running Away From the Barn G
神奇的树上差分
因为只求子树内的,所以第 i i i 个点的贡献只在这个点到根的链上
所以预处理每个点的深度,然后对每个点向上倍增找到最高的能产生贡献的点,链上都要 + 1 +1 + 1 ,做个差分即可。
COde
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 #include <bits/stdc++.h> #define int long long using namespace std;const int N = 2e5 + 5 ;int n, m;int f[N][25 ], dep[N];int val[N];int Find (int x) { int d = dep[x]; for (int i = 20 ; i >= 0 ; i--) if (d - dep[f[x][i]] <= m) x = f[x][i]; return f[x][0 ]; } signed main () { scanf ("%lld%lld" , &n, &m); for (int i = 2 ; i <= n; i++) { int w; scanf ("%lld%lld" , &f[i][0 ], &w); dep[i] = dep[f[i][0 ]] + w; for (int j = 1 ; j <= 20 ; j++) f[i][j] = f[f[i][j - 1 ]][j - 1 ]; } for (int i = n; i >= 1 ; i--) { int x = Find (i); val[x]--, val[i]++; } for (int i = n; i >= 1 ; i--) val[f[i][0 ]] += val[i]; for (int i = 1 ; i <= n; i++) printf ("%lld\n" , val[i]); return 0 ; }
USACO18FEB Snow Boots G
见 noip2021训练1 最后一题
USACO12FEB Nearby Cows G
非常的套路
分别求出每个点子树内的答案和子树外的答案,求和即可。
在求子树外的时候细节还是比较多
初始化数组不要 memset()
,那样复杂度就变成 O ( n 2 ) O(n^2) O ( n 2 ) 了,需要用多少就初始化多少。
Code
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 #include <bits/stdc++.h> #define pb push_back using namespace std;const int N = 1e5 + 5 ;int n, m, c[N];vector <int > G[N]; int f[N][25 ], g[N][25 ], pre[25 ], suf[N][25 ];void dfs1 (int u, int fa) { for (int i = 0 ; i <= m; i++) f[u][i] = c[u]; for (auto v : G[u]) { if (v == fa) continue ; dfs1 (v, u); for (int i = 1 ; i <= m; i++) f[u][i] += f[v][i - 1 ]; } return ; } void dfs2 (int u, int fa) { vector <int > ch; for (auto v : G[u]) if (v != fa) ch.pb (v); memset (pre, 0 , sizeof (pre)); for (int i = 0 ; i <= (int )ch.size (); i++) for (int j = 0 ; j <= m; j++) suf[i][j] = 0 ; for (int i = (int )ch.size () - 1 ; i >= 0 ; i--) { int v = ch[i]; for (int j = 0 ; j <= m; j++) suf[i][j] = suf[i + 1 ][j] + f[v][j]; for (int j = 1 ; j <= m; j++) g[v][j] = c[u]; } for (int i = 0 ; i < (int )ch.size (); i++) { int v = ch[i]; for (int j = 2 ; j <= m; j++) g[v][j] += pre[j - 2 ] + suf[i + 1 ][j - 2 ] + g[u][j - 1 ]; for (int j = 0 ; j <= m; j++) pre[j] += f[v][j]; } for (auto v : ch) dfs2 (v, u); return ; } int main () { scanf ("%d%d" , &n, &m); for (int i = 1 , u, v; i < n; i++) { scanf ("%d%d" , &u, &v); G[u].pb (v), G[v].pb (u); } for (int i = 1 ; i <= n; i++) scanf ("%d" , &c[i]); dfs1 (1 , 0 ); dfs2 (1 , 0 ); for (int i = 1 ; i <= n; i++) printf ("%d\n" , f[i][m] + g[i][m]); return 0 ; }
USACO05DEC Layout G
差分约束板子题
d i s x − d i s y ≤ w d i s x ≤ d i s y + w a d d ( y , x , w ) dis_x-dis_y\le w\qquad dis_x\le dis_y+w\qquad add(y,x,w) d i s x − d i s y ≤ w d i s x ≤ d i s y + w a d d ( y , x , w )
d i s x − d i s y ≥ w d i s y ≤ d i s x − w a d d ( x , y , − w ) dis_x-dis_y\ge w\qquad dis_y\le dis_x-w\qquad add(x,y,-w) d i s x − d i s y ≥ w d i s y ≤ d i s x − w a d d ( x , y , − w )
有负环无解,d i s n = i n f dis_n=inf d i s n = i n f 可以无穷远
(很久以前的代码了)
Code
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 #include <iostream> #include <cstdio> #include <cstring> #include <queue> #define N 1010 #define M 21010 #define INF 0x3f3f3f3f using namespace std;int n,ml,md;struct edge { int v,w,nxt; }e[M]; int tot,head[N];void add (int a,int b,int d) { e[++tot]=(edge){b,d,head[a]}; head[a]=tot; } int dis[N],vis[N],cnt[N];void spfa (int S) { memset (vis,0 ,sizeof (vis)); memset (cnt,0 ,sizeof (cnt)); for (int i=0 ; i<=n; i++) dis[i]=(i==S)?0 :INF; queue<int >que; vis[S]=1 ; que.push (S); while (!que.empty ()) { int u=que.front (); que.pop (); vis[u]=0 ; if ((++cnt[u])>=n) { printf ("-1\n" ); exit (0 ); } for (int i=head[u]; i; i=e[i].nxt) { int v=e[i].v; if (dis[v]>dis[u]+e[i].w) { dis[v]=dis[u]+e[i].w; if (!vis[v]) { que.push (v); vis[v]=1 ; } } } } } int main () { scanf ("%d%d%d" ,&n,&ml,&md); for (int i=1 ; i<=ml; i++) { int a,b,d; scanf ("%d%d%d" ,&a,&b,&d); add (a,b,d); } for (int i=1 ; i<=md; i++) { int a,b,d; scanf ("%d%d%d" ,&a,&b,&d); add (b,a,-d); } for (int i=1 ; i<=n; i++) add (0 ,i,0 ); spfa (0 ); spfa (1 ); if (dis[n]==INF) printf ("-2\n" ); else printf ("%d\n" ,dis[n]); return 0 ; }
USACO12OPEN Bookshelf G
考虑朴素 dp
设 f i f_i f i 表示放了前 i i i 本书,书架高度的最小值。
f i = m i n { f j − 1 + m a x j ≤ k ≤ i { h k } } ( ∑ p = j i w p ≤ L ) f_i=min\{f_{j-1}+max_{j\le k\le i}\{h_k\}\}\ (\sum_{p=j}^iw_p\le L) f i = m i n { f j − 1 + m a x j ≤ k ≤ i { h k } } ( ∑ p = j i w p ≤ L )
这样是 O ( n 2 ) O(n^2) O ( n 2 ) 的,考虑优化。
因为我们要求区间最小值,不难想到用线段树维护,但是有些困难。
需要用线段树维护 f f f 和 f + h f+h f + h 的最小值,用 v a l val v a l 存 f f f 的最小值,m n mn m n 存 f + h f+h f + h 的最小值。
对于每层书架,高度为最高的那本书,所以可以用单调栈处理出第 i i i 本书在前面多少本书中是最高的。然后只需要区间修改。
注意 v a l i val_i v a l i 的值应改为 f i − 1 f_{i-1} f i − 1 ,原因见上面转移方程。
所以只需要区间修改 、单点修改 、区间查询 。
在查询时的左端点可以用指针扫一遍,类似于单调队列。
Code
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 #include <bits/stdc++.h> #define ll long long using namespace std;const int N = 1e5 + 5 ;int n, m, h[N], w[N];int stk[N], top;ll f[N]; namespace seg{ #define INF 1e18 #define ls (rt << 1) #define rs (rt << 1 | 1) ll val[N << 2 ], mn[N << 2 ], tag[N << 2 ]; void pushup (int rt) { val[rt] = min (val[ls], val[rs]); mn[rt] = min (mn[ls], mn[rs]); } void pushdown (int rt) { if (~tag[rt]) { mn[ls] = val[ls] + tag[rt]; mn[rs] = val[rs] + tag[rt]; tag[ls] = tag[rs] = tag[rt]; tag[rt] = -1 ; } } void build (int l, int r, int rt) { val[rt] = mn[rt] = INF; tag[rt] = -1 ; if (l == r) return ; int mid = (l + r) >> 1 ; build (l, mid, ls); build (mid + 1 , r, rs); pushup (rt); } void init (int pos, int l, int r, int rt) { if (l == r) { val[rt] = f[l - 1 ], mn[rt] = INF; return ; } pushdown (rt); int mid = (l + r) >> 1 ; if (pos <= mid) init (pos, l, mid, ls); else init (pos, mid + 1 , r, rs); pushup (rt); } void update (int L, int R, ll h, int l, int r, int rt) { if (l > R || r < L) return ; if (L <= l && r <= R) { mn[rt] = val[rt] + h; tag[rt] = h; return ; } pushdown (rt); int mid = (l + r) >> 1 ; update (L, R, h, l, mid, ls); update (L, R, h, mid + 1 , r, rs); pushup (rt); } ll query (int L, int R, int l, int r, int rt) { if (l > R || r < L) return INF; if (L <= l && r <= R) return mn[rt]; pushdown (rt); int mid = (l + r) >> 1 ; return min (query (L, R, l, mid, ls), query (L, R, mid + 1 , r, rs)); } } template <typename T>void read (T &x) { x = 0 ; char c = getchar (); while (!isdigit (c)) c = getchar (); while (isdigit (c)) x = x * 10 + c - '0' , c = getchar (); return ; } int main () { read (n), read (m); seg::build (1 , n, 1 ); ll sum = 0 ; stk[++top] = 1 ; for (int i = 1 , l = 1 ; i <= n; i++) { read (h[i]), read (w[i]); seg::init (i, 1 , n, 1 ); while (top && h[i] >= h[stk[top]]) top--; seg::update (stk[top] + 1 , i, h[i], 1 , n, 1 ); stk[++top] = i; sum += w[i]; while (l < i && sum > m) sum -= w[l++]; f[i] = seg::query (l, i, 1 , n, 1 ); } printf ("%lld\n" , f[n]); return 0 ; }
USACO08MAR Land Acquisition G
斜率优化板子题,暑假的时候学长讲过,但当时没写 qaq
我把 x , y x,y x , y 分别当作长和宽
首先可以将被包含的矩形去掉,然后剩下的矩形 x x x 递增,y y y 递减
设 f i f_i f i 表示前 i i i 个矩形的最小代价
f i = m i n { f j − 1 + x i × y j } ( 1 ≤ j ≤ i ) f_i=min\{f_{j-1}+x_i\times y_j\}\ (1\le j \le i) f i = m i n { f j − 1 + x i × y j } ( 1 ≤ j ≤ i )
现在已经有了 O ( n 2 ) O(n^2) O ( n 2 ) 的暴力 dp,考虑优化
比较显然的斜率优化,先将式子变形一下
f j − 1 = − x i × y i + f i f_{j-1}=-x_i\times y_i+f_i f j − 1 = − x i × y i + f i
把 y i y_i y i 看作 x x x ,f j − 1 f_{j-1} f j − 1 看作 y y y ,x i x_i x i 看作 k k k ,f i f_i f i 看作 b b b
然后就得到了一次函数的基本形式 y = − k × x + b y=-k\times x + b y = − k × x + b
k k k 是已知的 x i x_i x i 并且递增,现在就是要最小化截距。
要维护一个下凸包,将已知斜率的一次函数与下凸包相切,就是最小的截距了。
维护下凸包可以用单调队列维护一个斜率递增的区间,当队首的斜率小于 x i x_i x i 时就已经没用了,因为后面的斜率递增,不可能再碰到队首了。
入队时只需要判断一下 队尾与队尾前一个点的斜率 和 ( y i , f i − 1 ) (y_i,f_{i-1}) ( y i , f i − 1 ) 与队尾的斜率 哪个大。
这个画图模拟一下就很好理解了,大概就是一个在一次象限的下凸包和斜率为负的一函数。
注意斜率是负的。
Code
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 #include <bits/stdc++.h> #define ll long long #define db double using namespace std;const int N = 5e4 + 5 ;int n;struct node { ll x, y; friend bool operator < (node a, node b) { return a.y == b.y ? a.x > b.x : a.y > b.y; } }a[N]; int cnt, l, r, q[N];db k[N]; ll f[N]; db slope (int i, int j) { return (db)(f[j - 1 ] - f[i - 1 ]) / (db)(a[i].y - a[j].y); } int main () { scanf ("%d" , &n); for (int i = 1 ; i <= n; i++) scanf ("%lld%lld" , &a[i].x, &a[i].y); sort (a + 1 , a + 1 + n); for (int i = 1 ; i <= n; i++) if (a[cnt].x < a[i].x) a[++cnt] = a[i]; l = 1 , r = 0 ; for (int i = 1 ; i <= cnt; i++) { while (l < r && k[r - 1 ] >= slope (q[r], i)) r--; k[r] = slope (q[r], i), q[++r] = i; while (l < r && k[l] <= a[i].x) l++; f[i] = f[q[l] - 1 ] + a[i].x * a[q[l]].y; } printf ("%lld\n" , f[cnt]); return 0 ; }
USACO17FEB Why Did the Cow Cross the Road III G
看到这题第一反应就是树状数组,然后试了试,过了样例,写完就过了(?
其实就是求每个数两次出现的位置间有多少个数出现了一次。
用树状数组维护前缀和,如果 a a a 是第一次出现,直接 + 1 +1 + 1 ,否则将答案加上两次出现位置中间的和。
其实就是中间出现了一次的数的个数。
Code
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 #include <bits/stdc++.h> #define ll long long using namespace std;const int N = 1e5 + 5 ;int n, a[N], pos[N];ll c[N], ans; void add (int x, int y) { for (; x <= n; x += x & -x) c[x] += y; } ll qry (int x) { ll res = 0 ; for (; x; x -= x & -x) res += c[x]; return res; } int main () { scanf ("%d" , &n); n <<= 1 ; for (int i = 1 ; i <= n; i++) { int a; scanf ("%d" , &a); if (!pos[a]) add (i, 1 ), pos[a] = i; else add (pos[a], -1 ), ans += qry (i) - qry (pos[a]); } printf ("%lld\n" , ans); return 0 ; }
USACO13JAN Seating G
比较套路的线段树题
维护区间最长连续 1 1 1 的个数
分别维护最左边,最右边最长的长度,然后合并一下。
在查询时先找左边,再找中间(将两边合并起来),最后找右边。
Code
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 #include <bits/stdc++.h> using namespace std;const int N = 5e5 + 5 ;int n, m, ans;namespace seg{ #define ls (rt << 1) #define rs (rt << 1 | 1) int mx[N << 2 ], lmx[N << 2 ], rmx[N << 2 ]; int tag[N << 2 ], len[N << 2 ]; void pushup (int rt) { lmx[rt] = lmx[ls]; rmx[rt] = rmx[rs]; if (lmx[rt] == len[ls]) lmx[rt] += lmx[rs]; if (rmx[rt] == len[rs]) rmx[rt] += rmx[ls]; mx[rt] = max (max (mx[ls], mx[rs]), rmx[ls] + lmx[rs]); } void build (int l, int r, int rt) { len[rt] = r - l + 1 ; tag[rt] = -1 ; mx[rt] = lmx[rt] = rmx[rt] = len[rt]; if (l == r) return ; int mid = (l + r) >> 1 ; build (l, mid, ls); build (mid + 1 , r, rs); } void pushdown (int rt) { if (tag[rt] != -1 ) { mx[ls] = lmx[ls] = rmx[ls] = tag[rt] ? len[ls] : 0 ; mx[rs] = lmx[rs] = rmx[rs] = tag[rt] ? len[rs] : 0 ; tag[ls] = tag[rs] = tag[rt]; tag[rt] = -1 ; } } void update (int L, int R, int v, int l, int r, int rt) { if (l > R || r < L) return ; if (L <= l && r <= R) { mx[rt] = lmx[rt] = rmx[rt] = v ? len[rt] : 0 ; tag[rt] = v; return ; } pushdown (rt); int mid = (l + r) >> 1 ; update (L, R, v, l, mid, ls); update (L, R, v, mid + 1 , r, rs); pushup (rt); } int query (int p, int l, int r, int rt) { if (l == r) return l; pushdown (rt); int mid = (l + r) >> 1 ; if (mx[ls] >= p) return query (p, l, mid, ls); if (rmx[ls] + lmx[rs] >= p) return mid - rmx[ls] + 1 ; return query (p, mid + 1 , r, rs); } } int main () { scanf ("%d%d" , &n, &m); seg :: build (1 , n, 1 ); while (m--) { char op[5 ]; scanf ("%s" , op); if (op[0 ] == 'A' ) { int p; scanf ("%d" , &p); if (seg :: mx[1 ] < p) { ans++; continue ; } int l = seg :: query (p, 1 , n, 1 ); seg :: update (l, l + p - 1 , 0 , 1 , n, 1 ); } else { int a, b; scanf ("%d%d" , &a, &b); seg :: update (a, b, 1 , 1 , n, 1 ); } } printf ("%d\n" , ans); return 0 ; }