预期题目定位
签到题:I, B, C, H \text{I, B, C, H} I, B, C, H
铜牌题:D, F, G, E \text{D, F, G, E} D, F, G, E
银牌题:A \text{A} A
PS:题解题目顺序为出题人主观难度递增顺序
出题人:MarisaMagic
题意简述
输入 n n n 个字符,判断有多少种字符。
解题思路 (语法)
用集合 std::set
存储字符或者用一个数组维护字符是否出现最后输出答案即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <bits/stdc++.h> using namespace std;void marisa () { int n; cin >> n; set<char > st; for (int i = 0 ; i < n; i ++ ){ char ch; cin >> ch; st.insert (ch); } cout << st.size () << "\n" ; }int main () { int T = 1 ; cin >> T; while (T -- ) marisa (); return 0 ; }
出题人:South_Orange
题意简述
给定 n n n 个魔法硬币,第 i i i 枚等级为 a i a_i a i ,两个相同等级的硬币可以合成一个高一级的硬币,求可以获得的最高等级的硬币。
解题思路 (模拟、思维)
开一个桶记录各个等级硬币的数量,再从小到大合成,硬币的最大等级应该为 max a i + l o g 2 n = 201740 \max {a_i} + {log_2n} = 201740 max a i + l o g 2 n = 201740 。合成过程中注意遍历到上限即可。
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 <iostream> using namespace std;#include <vector> #include <algorithm> #include <set> #include <map> #include <queue> #include <cmath> #include <cstring> #include <iomanip> using ll = long long ;const int N = 202010 ;int n;int cnt[N];void solve () { cin >> n; for (int i = 1 ;i <= n;i++) { int t; cin >> t; cnt[t]++; } int mx = 0 ; for (int i = 0 ;i <= 202000 ;i++) { if (cnt[i] != 0 ) { mx = i; cnt[i + 1 ] += cnt[i] / 2 ; } } cout << mx << '\n' ; }int main () { ios::sync_with_stdio (0 );cin.tie (0 );cout.tie (0 ); int T = 1 ; while (T--) { solve (); } return 0 ; }
出题人:MarisaMagic
题意简述
给定一个长度为 n n n 的排列 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a 1 , a 2 , … , a n 和 m m m 个栈,将排列元素依次放入栈中,求使得全部元素放入后能够以 1 , 2 , … , n 1, 2, \ldots, n 1 , 2 , … , n 的顺序依次出栈的最小的 m m m 。当取出某一个栈 S S S 中的一个元素时,不能取出任何其它栈中的元素直到栈 S S S 变为空。
解题思路 (模拟,栈)
假设一个栈中的元素从栈底到栈顶依次为 b 1 , b 2 , … , b t o p b_1, b_2, \ldots, b_{top} b 1 , b 2 , … , b t o p ,显然有 b i − 1 = b i + 1 b_{i - 1} = b_{i} + 1 b i − 1 = b i + 1 (1 < i ≤ t o p 1 < i \le top 1 < i ≤ t o p )。
若当前放入的元素为 x x x ,如果 x + 1 x + 1 x + 1 是一个栈 S S S 的栈顶元素,那么 x x x 就可以放入栈 S S S 中;否则,需要新开一个栈,将 x x x 放入新开的栈中。
我们可以维护每个元素所在栈的编号,最终最大栈的编号就是答案。时间复杂度为 O ( n ) \mathcal{O}(n) O ( n ) 。
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 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) void marisa () { int n; cin >> n; vector<int > id (n + 5 ) ; int m = 0 ; for (int i = 1 , x; i <= n; i ++ ){ cin >> x; if (!id[x + 1 ]) id[x] = ++ m; else id[x] = id[x + 1 ]; } cout << m << "\n" ; }int main () { ios; int T = 1 ; cin >> T; while (T -- ) marisa (); return 0 ; }
出题人:South_Orange
题意简述
给定一个数组,求数组中使得 f ( l , r ) = ⌊ ∑ i = l r a i r − l + 1 ⌋ f(l,r)=\left \lfloor \frac{ \sum_{i=l}^ra_i}{r-l+1} \right \rfloor f ( l , r ) = ⌊ r − l + 1 ∑ i = l r a i ⌋ 的值最大的 ( l , r ) (l,r) ( l , r ) 的对数。
解题思路 (组合数学)
易证 f ( l , r ) f(l,r) f ( l , r ) 的最大值的区间中的所有数都是数组中的最大数。
遍历数组,记录所有最大数的区间长度,对于每一个区间中最大数的个数 c n t cnt c n t ,都会为答案提供 c n t × ( c n t + 1 ) 2 \frac{cnt \times (cnt + 1)}{2} 2 c n t × ( c n t + 1 ) 对 ( l , r ) (l,r) ( l , r ) ,全部相加即是答案。
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 #include <iostream> using namespace std;#include <vector> #include <algorithm> #include <set> #include <map> #include <queue> #include <cmath> #include <cstring> #include <iomanip> using ll = long long ;const int N = 2e5 + 10 ;int n; ll a[N];void solve () { cin >> n; ll mx = -1e10 ; for (int i = 1 ;i <= n;i++) cin >> a[i], mx = max (mx, a[i]); ll cnt = 0 ; ll ans = 0 ; for (int i = 1 ;i <= n;i++) { if (a[i] == mx) cnt++; else { ans += cnt * (cnt + 1 ) / 2 ; cnt = 0 ; } } ans += cnt * (cnt + 1 ) / 2 ; cout << ans << '\n' ; }int main () { ios::sync_with_stdio (0 );cin.tie (0 );cout.tie (0 ); int T = 1 ; while (T--) { solve (); } return 0 ; }
出题人:MarisaMagic
题意简述
在二维坐标系上有 n n n 个红点和 m m m 个蓝点,无重合点。若一个点被另一个点攻击当且仅当:
两个点颜色不同;
两点 x x x 坐标或 y y y 坐标相同;
两点之间构成的线段上没有其它点。
判断哪些点是被攻击的。
解题思路 (排序)
设红点的值为 0 0 0 ,蓝点的值为 1 1 1 ,存储每个点的坐标、值和编号。先按照 x x x 坐标为第一关键字,y y y 为第二关键字排序,枚举相邻两个点,如果 x x x 相同且值不同,那么这两点是被攻击的;再按照 y y y 坐标为第一关键字,x x x 为第二关键字排序,枚举相邻两个点,如果 y y y 相同且值不同,那么这两点也是被攻击的。最后按要求输出答案,时间复杂度 O ( ( n + m ) log ( n + m ) ) \mathcal{O}((n + m)\log (n + m)) O (( n + m ) log ( n + m )) 。
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 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) struct node { int x, y, val, id; }; void marisa () { int n, m; cin >> n >> m; vector<node> p (n + m + 1 ) ; for (int i = 1 ; i <= n; i ++ ){ cin >> p[i].x >> p[i].y; p[i].val = 0 , p[i].id = i; } for (int i = n + 1 ; i <= n + m; i ++ ){ cin >> p[i].x >> p[i].y; p[i].val = 1 , p[i].id = i; } vector<bool > atk (n + m + 1 ) ; sort (begin (p) + 1 , end (p), [](const node& a, const node& b){ if (a.x != b.x) return a.x < b.x; return a.y < b.y; }); for (int i = 1 ; i < n + m; i ++ ){ if (p[i].x == p[i + 1 ].x && p[i].val != p[i + 1 ].val){ atk[p[i].id] = atk[p[i + 1 ].id] = true ; } } sort (begin (p) + 1 , end (p), [](const node& a, const node& b){ if (a.y != b.y) return a.y < b.y; return a.x < b.x; }); for (int i = 1 ; i < n + m; i ++ ){ if (p[i].y == p[i + 1 ].y && p[i].val != p[i + 1 ].val){ atk[p[i].id] = atk[p[i + 1 ].id] = true ; } } for (int i = 1 ; i <= n + m; i ++ ){ cout << atk[i]; if (i == n) cout << "\n" ; } }int main () { ios; int T = 1 ; while (T -- ) marisa (); return 0 ; }
出题人:MarisaMagic
题意简述
给定一个环形序列 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a 1 , a 2 , … , a n 。每次操作可以选择一个下标 x x x 使得 a x ← a x − a ( x m o d n ) + 1 a_x \leftarrow a_x - a_{(x \bmod n) + 1} a x ← a x − a ( x mod n ) + 1 ,并删除 a ( x m o d n ) + 1 a_{(x \bmod n) + 1} a ( x mod n ) + 1 。求最终剩下元素的最大值。
解题思路 (模拟,思维,数学)
考虑 全部为正数 的情况(包括 0 0 0 )。我们可以选定序列中最小的数 a m i n a_{min} a min ,然后用 a m i n a_{min} a min 依次减去其后面的 n − 2 n - 2 n − 2 个数得到一个元素 x x x ,再用最后剩下的 1 1 1 个数减去这个元素 x x x 。由此答案为 前 n − 1 n - 1 n − 1 大的数减去最小的数 ,即 ∑ i = 1 n a i − 2 × a m i n \sum_{i=1}^{n} a_i - 2 \times a_{min} ∑ i = 1 n a i − 2 × a min 。没有比这个更大的结果。
考虑 全部为负数 的情况(包括 0 0 0 )。我们可以选定序列中最大的数 a m a x a_{max} a ma x ,用这个数依次减去 n − 1 n - 1 n − 1 个数,最终剩下的一个元素就是答案,即 最大的数减去前 n − 1 n - 1 n − 1 小的数 ,即 ∑ i = 1 n ∣ a i ∣ + 2 × a m a x \sum_{i=1}^{n}|a_i| + 2 \times a_{max} ∑ i = 1 n ∣ a i ∣ + 2 × a ma x 。没有比这个更大的结果。
其实 全部为负数 的情况也可以通过看作是 全部为正数 ,将每个 a i a_i a i 变为其绝对值即可,可以统一这两个情况的答案为 ∑ i = 1 n ∣ a i ∣ − 2 × ∣ a i ∣ m i n \sum_{i=1}^{n}|a_i| - 2 \times |a_i|_{min} ∑ i = 1 n ∣ a i ∣ − 2 × ∣ a i ∣ min 。
考虑 有正数也有负数 的情况。我们一定可以找到一种方法使得最终答案为序列所有数绝对值之和,即 ∑ i = 1 n ∣ a i ∣ \sum_{i=1}^{n}|a_i| ∑ i = 1 n ∣ a i ∣ 。一种可行的方法为:找到环形序列中一个在负数前面的正数,选定这个正数,将剩余其它的数全部转变为负数 ,最后再用这个正数依次减去剩下的负数,最终答案就是 ∑ i = 1 n ∣ a i ∣ \sum_{i=1}^{n}|a_i| ∑ i = 1 n ∣ a i ∣ 。
例如环形序列为 { − 1 , 2 , − 3 , 1 , 2 , − 2 , 3 } \{ -1, 2, -3, 1, 2, -2, 3 \} { − 1 , 2 , − 3 , 1 , 2 , − 2 , 3 } ,我们按照上面的思路,选定 a 2 = 2 a_2 = 2 a 2 = 2 这个正数,序列变化为:{ − 1 , 2 , − 3 ‾ , 1 ‾ , 2 , − 2 , 3 } \{ -1, {\color{red}{2}}, \underline{-3}, \underline{1}, 2, -2, 3 \} { − 1 , 2 , − 3 , 1 , 2 , − 2 , 3 } → \rightarrow → { − 1 , 2 , − 4 ‾ , 2 ‾ , − 2 , 3 } \{ -1, {\color{red}{2}}, \underline{-4}, \underline{2}, -2, 3 \} { − 1 , 2 , − 4 , 2 , − 2 , 3 } → \rightarrow → { − 1 , 2 , − 6 , − 2 ‾ , 3 ‾ } \{ -1, {\color{red}{2}}, -6, \underline{-2}, \underline{3} \} { − 1 , 2 , − 6 , − 2 , 3 } → \rightarrow → { − 1 , 2 , − 6 , − 5 } \{ -1, {\color{red}{2}}, -6, -5 \} { − 1 , 2 , − 6 , − 5 } → \rightarrow → { 14 } \{ 14 \} { 14 } 。
综上所述 :
当 全部为正数 或者 全部为负数 时,答案为 ∑ i = 1 n ∣ a i ∣ − 2 × ∣ a i ∣ m i n \sum_{i=1}^{n}|a_i| - 2 \times |a_i|_{min} ∑ i = 1 n ∣ a i ∣ − 2 × ∣ a i ∣ min ;
否则,答案为 ∑ i = 1 n ∣ a i ∣ \sum_{i=1}^{n}|a_i| ∑ i = 1 n ∣ a i ∣ 。
特别地 ,当 n = 1 n = 1 n = 1 时,答案是 a 1 a_1 a 1 。时间复杂度 O ( n ) \mathcal{O}(n) O ( n )
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 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) using ll = long long ;void marisa () { int n; cin >> n; vector<int > a (n + 1 ) ; ll sum = 0 ; int mn = 1e9 ; bool all_minus = true , all_plus = true ; for (int i = 1 ; i <= n; i ++ ){ cin >> a[i]; sum += abs (a[i]); mn = min (mn, abs (a[i])); if (a[i] > 0 ) all_minus = false ; if (a[i] < 0 ) all_plus = false ; } if (n == 1 ){ cout << a[1 ] << "\n" ; return ; } if (all_plus || all_minus) cout << sum - 2 * mn << "\n" ; else cout << sum << "\n" ; }int main () { ios; int T = 1 ; cin >> T; while (T -- ) marisa (); return 0 ; }
出题人:MarisaMagic
题意简述
给定 n n n 个不同的点 ( x i , y i ) (x_i, y_i) ( x i , y i ) ,可以任意添加 m m m 个点。求满足 x i + 1 − x i = 1 , y i + 1 = y i x_{i+1} - x_i = 1, y_{i+1} = y_{i} x i + 1 − x i = 1 , y i + 1 = y i 或者 x i + 1 = x i , y i + 1 − y i = 1 x_{i+1} = x_i, y_{i+1} - y_i = 1 x i + 1 = x i , y i + 1 − y i = 1 的最长上升点列的长度。
解题思路 (动态规划 DP)
考虑动态规划,设 f i , k f_{i,k} f i , k 为以第 i i i 个点为结尾,前面加入 k k k 个点的最长上升点列长度。由此可得状态转移方程:
f i , k = max ( f j , k − d + d + 1 ) , 1 ≤ i ≤ n , 0 ≤ k ≤ m , 1 ≤ j < i f_{i,k} = \max(f_{j, k - d} + d + 1), \ \ \ 1 \le i \le n, \ 0 \le k \le m, \ 1 \le j < i
f i , k = max ( f j , k − d + d + 1 ) , 1 ≤ i ≤ n , 0 ≤ k ≤ m , 1 ≤ j < i
其中 d = ∣ x i − x j ∣ + ∣ y i − y j ∣ − 1 d = |x_i - x_j| + |y_i - y_j| - 1 d = ∣ x i − x j ∣ + ∣ y i − y j ∣ − 1 表示从点 ( x j , y j ) (x_j, y_j) ( x j , y j ) 到 点 ( x i , y i ) (x_i, y_i) ( x i , y i ) 所需添加的点数。
在更新 f i , k f_{i, k} f i , k 的状态时,需要确保前面所有 x x x 坐标小于 x i x_i x i 的点结尾的最长点列长度被计算出来,还要确保 x x x 坐标相同时,前面所有 y y y 坐标小于 y i y_i y i 的点结尾的最长点列长度被计算出来。故我们在进行 dp 前,需要先对所有点以 x x x 为第一关键字递增,以 y y y 为第二关键字递增排序 。
我们还可以随意往后添加任意 m − k m - k m − k 个点,故最终答案为 max ( f i , k + ( m − k ) ) \max(f_{i, k} + (m - k)) max ( f i , k + ( m − k )) 。时间复杂度 O ( n 2 m ) \mathcal{O}(n^2 m) O ( n 2 m ) 。
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> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) #define x first #define y second void marisa () { int n, m; cin >> n >> m; vector<pair<int , int >> p (n + 1 ); for (int i = 1 ; i <= n; i ++ ) cin >> p[i].x >> p[i].y; sort (begin (p) + 1 , end (p)); int ans = 0 ; vector<vector<int >> f (n + 1 , vector <int >(m + 1 )); for (int i = 1 ; i <= n; i ++ ) f[i][0 ] = 1 ; for (int i = 1 ; i <= n; i ++ ){ for (int k = 0 ; k <= m; k ++ ){ for (int j = 1 ; j < i; j ++ ){ if (p[i].y < p[j].y) continue ; int d = (p[i].x - p[j].x) + (p[i].y - p[j].y) - 1 ; if (k >= d) f[i][k] = max (f[i][k], f[j][k - d] + d + 1 ); } ans = max (ans, f[i][k] + (m - k)); } } cout << ans << "\n" ; }int main () { ios; int T = 1 ; while (T -- ) marisa (); return 0 ; }
出题人:MarisaMagic
题意简述
给定一个 n n n 点 m m m 条边的简单无向图,第 i i i 条边长度为 2 i 2^i 2 i ,求长度最小的简单环 且环边数量至少为 3 3 3 。递增输出环上边的编号,若无解则输出 − 1 -1 − 1 。
解题思路 (贪心,生成树,搜索)
考虑贪心,从小到大枚举每条边。由于 ∑ i = 1 k 2 i < 2 k + 1 \sum_{i=1}^k 2^i < 2^{k+1} ∑ i = 1 k 2 i < 2 k + 1 ,故贪心思路正确。使用 Kruskal 算法,用并查集维护:
如果加入第 i i i 条边到生成树不会构成环,则加入到生成树中;
否则,第 i i i 条边就是构成最小环的最后一条边。记录 u i u_i u i 和 v i v_i v i 分别为起点和终点,并退出枚举。
然后用 dfs 从起点开始搜索找到生成树上到终点的路径,在搜索过程中加入边到答案中。最后加上最后一条边,按要求输出答案。时间复杂度 O ( n log n ) \mathcal{O}(n \log n) O ( n log n ) 。
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> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) using pii = pair<int , int >;const int N = 1e5 + 10 ;int fa[N];inline int find (int x) { return fa[x] == x ? x : fa[x] = find (fa[x]); }void marisa () { int n, m; cin >> n >> m; vector<pii> edges (m + 1 ) ; for (int i = 1 ; i <= m; i ++ ){ cin >> edges[i].first >> edges[i].second; } int src = 0 , des = 0 , last_i = 0 ; vector<vector<pii>> e (n + 1 ); for (int i = 1 ; i <= n; i ++ ) fa[i] = i; for (int i = 1 ; i <= m; i ++ ){ const auto & [u, v] = edges[i]; int fx = find (u), fy = find (v); if (fx == fy){ src = u, des = v; last_i = i; break ; } fa[fx] = fy; e[u].emplace_back (v, i); e[v].emplace_back (u, i); } if (!src){ cout << -1 << "\n" ; return ; } vector<int > ans, vec; function<void (int , int )> dfs = [&](int u, int fa){ if (u == des){ ans = vec; return ; } for (const auto & [v, i] : e[u]){ if (v == fa) continue ; vec.emplace_back (i); dfs (v, u); vec.pop_back (); } }; dfs (src, 0 ); ans.emplace_back (last_i); sort (begin (ans), end (ans)); for (size_t i = 0 ; i < ans.size (); i ++ ){ cout << ans[i] << " \n" [i == ans.size () - 1 ]; } }int main () { ios; int T = 1 ; cin >> T; while (T -- ) marisa (); return 0 ; }
出题人:South_Orange
题意简述
给点一个 n n n 行 m m m 列的棋盘,要求构造一种局面使得每列的棋子数为 a i a_i a i 且无任何两个棋子相邻。
解题思路 (构造)
由于限制的是每列多少个,于是来考虑行n n n 。
如果n n n 为偶数,每列都只能放 n 2 \frac n2 2 n 个,与相邻的列无关,直接判断即可。
否则至少能放 n 2 \frac n2 2 n 个,最多能放 n 2 + 1 \frac n2+1 2 n + 1 个,差异在于放在奇数位置上或偶数位置上 。
如果 max a i \max a_i max a i 超过了 n 2 + 1 \frac n2+1 2 n + 1 或小于 n 2 \frac n2 2 n 了就不再考虑,这些可以特判。
我们称,满足a i = n 2 + 1 a_i=\frac n2+1 a i = 2 n + 1 的列i i i 叫做顶满 了的列。
我们可以发现如果顶满了的列都在奇数列或偶数列,直接强制保证顶满的列都填奇数位置,由于其他列都未顶满,n 2 \frac n2 2 n 个位置就够用了,对于每列从前往后交替(注:交替指奇偶位置交替)填即可,没有影响。于是考虑某两个顶满的列 i , j i,j i , j 满足 i < j i<j i < j 满足 j − i ≡ 1 j- i\equiv 1 j − i ≡ 1 ( mod 2) ,即中间行数为偶行。
此时如果你不进行操作,你强制两列都放奇数位置,中间的列就可能出现问题,更具体的,你需要将 j − 1 j-1 j − 1 的奇数位置空出来,否则无法构造。
考虑构造一种方法让其空出来,由于原来不操作时,这些列是交替填的,我们希望让中间某两行打破这个交替。
那么我们就从后往前尽量让当前行不与上一行交替,可能会剩下一些必定交替,此时再正常的按交替的填,直到某两行打破交替,后面的构造就都会切换奇偶交替的状态了,使得 j − 1 j-1 j − 1 的奇数位置能空出来。
如果从 i i i 构造至 j j j 都还没构造出来则说明无解,否则用相同的方法处理后面顶满的列即可。
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 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 #include <iostream> using namespace std;#include <vector> #include <algorithm> #include <set> #include <map> #include <queue> #include <cmath> #include <cstring> #include <iomanip> using ll = long long ;const int N = 310 ;int n, m;int a[N];int b[N]; vector<int > mm;int ans[N][N];void solve () { cin >> n >> m; bool flag = false ; for (int i = 1 ;i <= m;i++) { cin >> a[i]; b[i] = a[i]; if (a[i] > (n + 1 ) / 2 ) flag = true ; else if (a[i] == (n + 1 ) / 2 ) { if (n % 2 == 1 && !mm.empty () && mm.back () == i - 1 ) flag = true ; else mm.push_back (i); } } if (flag) { cout << "No\n" ; return ; } if (n % 2 == 0 || mm.empty ()) { cout << "Yes\n" ; for (int j = 1 ;j <= m;j++) for (int i = 1 ;i <= n;i++) { if (b[j] && (i + j) % 2 == 0 ) { ans[i][j] = 1 ; b[j]--; } else if (b[j] == 0 ) break ; } for (int i = 1 ;i <= n;i++) { for (int j = 1 ;j <= m;j++) cout << ans[i][j]; cout << '\n' ; } return ; } for (auto j : mm) for (int i = 1 ;i <= n;i += 2 ) ans[i][j] = 1 ; for (int p = 1 ;p < mm.size ();p++) { if ((mm[p] - mm[p - 1 ]) % 2 == 1 ) { int pre = mm[p - 1 ], suf = mm[p]; for (int j = pre + 1 ;j < suf;j++) { int t, i; for (t = 1 , i = n - (((j - pre) & 1 ) ^ 1 );t <= a[j] && i > 0 ;t++, i -= 2 ) { if (ans[i][j - 1 ] || ans[i][j + 1 ]) break ; ans[i][j] = 1 ; } for (i = ((j - pre) & 1 ) + 1 ;t <= a[j];t++, i += 2 ) { if (ans[i][j - 1 ] || ans[i][j + 1 ]) { cout << "No\n" ; return ; } ans[i][j] = 1 ; } } } else { int pre = mm[p - 1 ] + 1 , suf = mm[p] - 1 ; for (int j = 0 ;j <= suf - pre;j++) { for (int i = 1 ;i <= n;i++) { if ((i + j) % 2 == 0 && b[pre + j]) { ans[i][pre + j] = 1 ; b[pre + j]--; } else if (b[pre + j] == 0 ) break ; } } } } int pre = mm.front () - 1 , suf = mm.back () + 1 ; for (int j = 0 ;pre - j > 0 ;j++) { for (int i = 1 ;i <= n;i++) if ((i + j) % 2 == 0 && b[pre - j]) { ans[i][pre - j] = 1 ; b[pre - j]--; } else if (b[pre - j] == 0 ) break ; } for (int j = 0 ;suf + j <= m;j++) { for (int i = 1 ;i <= n;i++) if ((i + j) % 2 == 0 && b[suf + j]) { ans[i][suf + j] = 1 ; b[suf + j]--; } else if (b[suf + j] == 0 ) break ; } cout << "Yes\n" ; for (int i = 1 ;i <= n;i++) { for (int j = 1 ;j <= m;j++) cout << ans[i][j]; cout << '\n' ; } }int main () { ios::sync_with_stdio (0 );cin.tie (0 );cout.tie (0 ); int T = 1 ; while (T--) { solve (); } return 0 ; }