# 题目原文Kostya has a text s s s consisting of n n n words made up of Latin alphabet letters. He also has two strips on which he must write the text. The first strip can hold m m m characters, while the second can hold as many as needed.
Kostya must choose a number x x x and write the first x x x words from s s s on the first strip, while all the remaining words are written on the second strip. To save space, the words are written without gaps, but each word must be entirely on one strip.
Since space on the second strip is very valuable, Kostya asks you to choose the maximum possible number x x x such that all words s 1 , s 2 , … , s x s_1, s_2, \dots, s_x s 1 , s 2 , … , s x fit on the first strip of length m m m .
# 题目大意给定 n n n 个长度确定的字符串 s i s_i s i ,要将其放置于两个磁带中,第一个磁带可以放置 m m m 个字符,第二个磁带无限长。已知每两个字符串之间不需要空格,若一个字符串不可以被分割成两部分放入两个磁带中,请问第一个磁带最多可以放置几个字符串。(并且,字符串还得是按顺序放置)
# 题解找前 x x x 个字符串,使得 x x x 最大的情况下,∑ i = 1 x s [ i ] . s i z e ( ) ≤ m \sum\limits_{i=1}^x s[i].size() \le m i = 1 ∑ x s [ i ] . s i z e ( ) ≤ m
# 注意,不能这样写
1 2 3 4 5 6 7 8 9 10 int cnt = 0 , i = 1 ; while (true ) { m -= s[i].size (); if (m < 0 ) break ; cnt++, i++; }
会 R E RE R E ,因为可能所有字符串都能放在第一个磁带中
# 正确写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void solve () { cin >> n >> m; for (int i = 1 ; i <= n; i++) cin >> s[i]; int cnt = 0 , i = 1 ; for (int i = 1 ; i <= n; i++) { m -= s[i].size (); if (m < 0 ) break ; cnt++; } cout << cnt << endl; }
# 题目大意对于一个序列 A = ( a 1 , a 2 , . . . , a n ) A=(a_1,a_2,...,a_n) A = ( a 1 , a 2 , . . . , a n ) ,你可以从 i = 2
→ \to → i = n - 1
中,任选一个 i i i ,进行两种操作
a[i - 1] += 1, a[i + 1] -= 1
a[i - 1] -= 1, a[i + 1] += 1
问,是否可以经过若干次操作后,使得 a i = ( ∑ j = 1 n a j ) / n a_i = (\sum\limits_{j = 1}^n a_j) / n a i = ( j = 1 ∑ n a j ) / n
# 题解很奇怪的是,题目内有一句是:After each operation, all the values must be non-negative. Can you make all the elements equal after any number of operations? 但是加上特判反倒 W A WA W A 没有特判 A C AC A 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 int n, a[N], avg = 0 ;void solve () { avg = 0 ; cin >> n; for (int i = 1 ; i <= n; i++) cin >> a[i], avg += a[i]; if (avg % n != 0 ) { cout << "NO" << endl; return ; } avg /= n; for (int i = 2 ; i <= n - 1 ; i++) { a[i + 1 ] += a[i - 1 ] - avg, a[i - 1 ] = avg; } if (a[n - 1 ] != avg || a[n] != avg) cout << "NO" << endl; else cout << "YES" << endl; }
# 题目大意给定一个长度不超过 1 0 5 10^5 1 0 5 的字符串,你可以对任意一个数字进行平方并且替换(前提是平方后的数字还是一位数) 问:有没有可能,经过若干次操作后(可以为 0 0 0 ),操作后的数是 9 9 9 的倍数?
# 错误解法一开始看错了题,以为是数值不超过 1 0 5 10^5 1 0 5 ,想当然的用了搜索,然后光荣 W A WA W A
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 bool flag;int len;string s; void dfs (int u, int num) { if (flag) return ; if (u == len + 1 ) { flag = (num % 9 == 0 ); return ; } int t = s[u] - '0' ; if (t <= 1 ) dfs (u + 1 , num * 10 + t); else { while (t < 10 ) { dfs (u + 1 , num * 10 + t); t *= t; } } } void solve () { flag = false ; cin >> s; len = s.size (), s = " " + s; dfs (1 , 0 ); cout << (flag ? "YES" : "NO" ) << endl; }
# 正解首先,能被 9 9 9 整除的数有这样一个性质: 其各位数的和是 9 的倍数
再者,题目说的是进行平方替换操作(前提是平方后的数字还是一位数),只有 1 , 2 , 3 1,2,3 1 , 2 , 3 可以进行这个操作,而 1 1 1 平方后不会影响原数,只有 2 2 2 和 3 3 3 平方会改变和,所以只需考虑 2 2 2 和 3 3 3 分别改变 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 void solve () { cin >> s; int cnt2 = 0 , cnt3 = 0 , sum = 0 ; for (char x : s) { sum += x - '0' ; if (x == '2' ) cnt2++; if (x == '3' ) cnt3++; } for (int i = 0 ; i <= cnt2; i++) for (int j = 0 ; j <= cnt3; j++) if ((2 * i + 6 * j + sum) % 9 == 0 ) { cout << "YES" << endl; return ; } cout << "NO" << endl; }
# 题目大意给定一个字符串 s s s (是一个数字),你可以进行一种操作:选择任意一个非 0 0 0 的、不是最左边的数字的,使其减一并且与左边一个对调位置,你可以进行任意次操作。 问:可以构成的最大数字是多少?
一共有 t ( 1 ≤ t ≤ 1 0 4 ) t~(1 \le t \le 10^4) t ( 1 ≤ t ≤ 1 0 4 ) 个数,每个数长度为 1 ≤ l e n ≤ 2 ⋅ 1 0 5 1 \le len \le 2\cdot 10^5 1 ≤ l e n ≤ 2 ⋅ 1 0 5
# 题解大水题啊,暴力搜就好,用一个 maxN
存每一位的数,对于当前遍历到的 t = a[i]
不断往前找,只要 t - 1 > 前一个数
,那么就继续往前,复杂度较可观,一个数最多往前 8 8 8 次,复杂度是 O ( 8 ⋅ t ⋅ l e n ) O(8 \cdot t \cdot len) O ( 8 ⋅ t ⋅ l e n )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void solve () { memset (maxN, 0 , sizeof maxN); cin >> (a + 1 ), n = strlen (a + 1 ); for (int i = 1 ; i <= n; i++) maxN[i] = a[i] - '0' ; for (int i = 2 ; i <= n; i++) if (maxN[i] > 1 ) { int t = maxN[i], j = i - 1 ; while (j >= 1 && t - 1 > maxN[j]) maxN[j + 1 ] = maxN[j], maxN[j] = t - 1 , t--, j--; } for (int i = 1 ; i <= n; i++) cout << maxN[i]; cout << endl; }
# 题目大意给定三个字符串 a , b , c a,b,c a , b , c
假想一个字符串 d d d ,d d d 的是这样构成的,每次任从 a o r b a~or~b a o r b 中选择一个字符串,将这个字符串的第一个字符挪到 d d d 的末尾并从选中字符串中删除最前面的字符。
问:在若干种可能构成的 d d d 字符串中,与 c c c 字符串相比较,最小不同字符数是多少?
t ( 1 ≤ t ≤ 1 0 3 ) t~(1 \le t \le 10^3) t ( 1 ≤ t ≤ 1 0 3 ) 组数据。 每组数据中,a , b a,b a , b 字符串的长度都满足 1 ≤ l e n ≤ 1 0 3 1 \le len \le 10^3 1 ≤ l e n ≤ 1 0 3
# 题解还是无法战胜噩梦 d p dp d p 吗
设 d p i j dp_{ij} d p i j 为字符串 a a a 的前 i i i 个字符和字符串 b b b 的前 j j j 个字符组成字符串 c c c 的前 i + j i+j i + j 个字符的最小替换次数
如果当前字符来源 a a a 如果当前字符是目标字符, dp[i][j] = dp[i - 1][j]
如果不是, dp[i][j] = dp[i - 1][j] + 1
如果当前字符来源 b b b 如果当前字符是目标字符, dp[i][j] = dp[i][j - 1]
如果不是, dp[i][j] = dp[i][j - 1] + 1
对于以上两种情况取 m i n min m i n 即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 string a, b, c; int dp[N][N];void solve () { memset (dp, 0x3f , sizeof dp); cin >> a >> b >> c; int la = a.size (), lb = b.size (), lc = c.size (); a = " " + a, b = " " + b, c = " " + c; dp[0 ][0 ] = 0 ; for (int i = 1 ; i <= la; i++) dp[i][0 ] = dp[i - 1 ][0 ] + (a[i] != c[i]); for (int i = 1 ; i <= lb; i++) dp[0 ][i] = dp[0 ][i - 1 ] + (b[i] != c[i]); for (int i = 1 ; i <= la; i++) for (int j = 1 ; j <= lb; j++) dp[i][j] = min (dp[i - 1 ][j] + (a[i] != c[i + j]), dp[i][j - 1 ] + (b[j] != c[i + j])); cout << dp[la][lb] << endl; }
# 题意给定一个 n n n 个数的序列 A ( a 1 , a 2 , . . . , a n ) A(a_1,a_2,...,a_n) A ( a 1 , a 2 , . . . , a n ) ,和 q q q 个查询,每次查询给定一个范围 l , r l,r l , r
问,对于 a l → a r a_l \to a_r a l → a r ,请你确定一个 m m m ,使得对于 ∀ i ∈ [ l , r ] \forall~i\in[l,r] ∀ i ∈ [ l , r ] 满足 a i % m = k a_i~\%~m = k a i % m = k ,k k k 是一个固定整数。如果当 m → + ∞ m \rightarrow +\infty m → + ∞ 时,则输出 0 0 0
一共 t , ( 1 ≤ t ≤ 1 0 4 ) t,~(1 \le t \le 10^4) t , ( 1 ≤ t ≤ 1 0 4 ) 组数据,1 ≤ n , q ≤ 2 ⋅ 1 0 5 1 \le n,q \le 2 \cdot 10^5 1 ≤ n , q ≤ 2 ⋅ 1 0 5 ,1 ≤ l , r ≤ n 1 \le l,r \le n 1 ≤ l , r ≤ n ,1 ≤ a i ≤ 1 0 9 1 \le a_i \le 10^9 1 ≤ a i ≤ 1 0 9
# 题解还是无法战胜噩梦数学题吗?
你需要知道:若 a > b,则 a 和 b 对 a - b 同余。 设 p = a − b , a = k p + q p=a-b,~a=kp+q p = a − b , a = k p + q 那么 b = a − ( a − b ) = a − p = ( k − 1 ) p + q b=a-(a-b)=a-p=(k-1)p+q b = a − ( a − b ) = a − p = ( k − 1 ) p + q a , b , p a,b,p a , b , p 模 p p p 都是 q q q ,即 a , b a,b a , b 对 a − b a-b a − b 同余(都是 q q q )
你还需要知道:若 a > b,则 a 和 b 对 a - b 的所有因数同余 设 p = a − b , a = k p + q , b = ( k − 1 ) p + q p=a-b,a=kp+q,b=(k-1)p+q p = a − b , a = k p + q , b = ( k − 1 ) p + q 设 p p p 的因数为 p 0 p_0 p 0 ,因为 k p kp k p 和 ( k − 1 ) p (k-1)p ( k − 1 ) p 可以被 p p p 整除,则这两个数都同余于 p 0 p_0 p 0 并且 q q q 和 q q q 同余于 p 0 p_0 p 0 所以 a , b a,b a , b 的两部分都同余于 p 0 p_0 p 0 综上,a , b a,b a , b 同余于 p 0 p_0 p 0
因此,a l a_l a l 到 a r a_r a r 同余可以拆分成:
限制 结论 a l , a l + 1 同余 a_l,a_{l+1}同余 a l , a l + 1 同 余 m m m 是 ∣ a l − a l + 1 ∣ 的因数 \vert{a_l-a_{l+1}}\vert 的因数 ∣ a l − a l + 1 ∣ 的 因 数 a l + 1 , a l + 2 同余 a_{l+1},a_{l+2}同余 a l + 1 , a l + 2 同 余 m m m 是 ∣ a l + 1 − a l + 2 ∣ 的因数 \vert{a_{l+1}-a_{l+2}}\vert 的因数 ∣ a l + 1 − a l + 2 ∣ 的 因 数 ... ... a r − 2 , a r − 1 同余 a_{r-2},a_{r-1}同余 a r − 2 , a r − 1 同 余 m m m 是 ∣ a r − 2 − a r − 1 ∣ 的因数 \vert{a_{r-2}-a_{r-1}}\vert 的因数 ∣ a r − 2 − a r − 1 ∣ 的 因 数 a r − 1 , a r 同余 a_{r-1},a_{r}同余 a r − 1 , a r 同 余 m m m 是 ∣ a r − 1 − a r ∣ 的因数 \vert{a_{r-1}-a_{r}}\vert 的因数 ∣ a r − 1 − a r ∣ 的 因 数
得出结论,m = g c d ( ∣ a l − a l + 1 ∣ , ∣ a l + 1 − a l + 2 ∣ , . . . , ∣ a r − 2 − a r − 1 ∣ , ∣ a r − 1 − a r ∣ ) m = gcd(\vert{a_{l}-a_{l+1}}\vert,\vert{a_{l+1}-a_{l+2}}\vert,...,\vert{a_{r-2}-a_{r-1}}\vert,\vert{a_{r-1}-a_{r}}\vert) m = g c d ( ∣ a l − a l + 1 ∣ , ∣ a l + 1 − a l + 2 ∣ , . . . , ∣ a r − 2 − a r − 1 ∣ , ∣ a r − 1 − a r ∣ ) ,需要特判,区间长度为 1 1 1
# 暴力写法暴力对每一个 l , r l,r l , r 求解 __gcd
,显然时间复杂度爆了:O ( T ⋅ Q ⋅ N ⋅ log a i ) O(T\cdot Q\cdot N\cdot \log{a_i}) O ( T ⋅ Q ⋅ N ⋅ log a i )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void solve () { cin >> n >> q; for (int i = 1 ; i <= n; i++) cin >> a[i]; while (q--) { cin >> l >> r; if (l == r) cout << "0 " ; else { int g = abs (a[l] - a[l + 1 ]); for (int i = l + 1 ; i <= r - 1 ; i++) g = __gcd(g, abs (a[i] - a[i + 1 ])); cout << g << " " ; } } cout << endl; }
# 正解
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 #include <bits/stdc++.h> using namespace std;int n, q, a[200005 ], s[200005 ];int l, r, Log[200005 ];struct ST_map { int Gcd[200005 ][21 ]; void Init () { for (int i = 1 ; i <= n - 1 ; i++) Gcd[i][0 ] = s[i]; for (int i = 1 ; i <= 20 ; i++) for (int j = 1 ; j <= n - 1 ; j++) Gcd[j][i] = __gcd(Gcd[j][i - 1 ], Gcd[min (j + (1 << (i - 1 )), n)][i - 1 ]); } int Query (int l, int r) { int logx = Log[r - l + 1 ]; return __gcd(Gcd[l][logx], Gcd[r - (1 << logx) + 1 ][logx]); } } ST; void Main () { scanf ("%d%d" , &n, &q); for (int i = 1 ; i <= n; i++) scanf ("%d" , &a[i]); for (int i = 1 ; i < n; i++) s[i] = abs (a[i + 1 ] - a[i]); ST.Init (); while (q--) { scanf ("%d%d" , &l, &r); if (l == r) printf ("0 " ); else printf ("%d " , ST.Query (l, r - 1 )); } puts ("" ); } signed main () { int cnt = 0 , last = 2 ; for (int i = 2 ; i <= 200000 ; i++) { if (i == last) cnt++, last *= 2 ; Log[i] = cnt; } int T; scanf ("%d" , &T); while (T--) Main (); return 0 ; }