本次比赛 链接
现在的水平只能碰碰 Div 3
A. Only Pluses三个数越相近,乘积就越大。因此五次递增每次分别作用于当前三个数中最小的那个。
B. Angry Monk除了当前最长的那一个片段之外,其他片段都必须要掰成一节节之后再拼上。这样总步数最小。
C. Gorilla and Permutation定义的两个函数 f ( i ) f(i) f ( i ) 表示数组前缀长度 i i i 中所有大于 k k k 的数字之和,g ( i ) g(i) g ( i ) 表示数组前缀长度 i i i 中所有小于 m m m 的数字之和。要想最大化 ∑ i ( f ( i ) − g ( i ) ) \sum_i(f(i)-g(i)) ∑ i ( f ( i ) − g ( i ) ) , 就必须要让 f ( i ) f(i) f ( i ) 尽量大,g ( i ) g(i) g ( i ) 尽量小。
所以构造的排列就是先是比 k k k 大的数字降序排列,中间的无所谓,之后是比 m m m 小的数字的升序排列。
D. Test of Love 题目大意给出一个长度为 n n n 的序列,其中包含水 W
、鳄鱼 C
和浮木 L
。如果在岸上或在浮木上,可以向前最多跳 m
格,可以跳到水里、浮木或岸上;如果当前在水里,则可以向前游一格,但整个过程中最多能游 k
格;无论如何都不能进入鳄鱼所在的格子。问:是否从起始位置到达彼岸。
解题思路暴搜。因为游是有限制的,跳是无限制的,所以能跳则跳。
如果当前在岸上或在浮木上,向前搜索选择跳到最远的浮木。如果前面 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 #include <bits/stdc++.h> using namespace std;int n,m,k;string river; bool BFS (int pos,int swim_remain) { if (swim_remain<0 )return false ; if (pos>=n)return true ; if (river[pos]=='C' )return false ; else if (river[pos]=='L' ){ for (int i=m;i>=1 ;i--){ if (pos+i>=n)return true ; else if (river[pos+i]=='L' ){return BFS (pos+i,swim_remain);} } for (int i=m;i>=1 ;i--){ if (pos+i>n)return true ; else if (river[pos+i]=='W' ){ return BFS (pos+i,swim_remain); } } return false ; } else if (river[pos]=='W' ){ return BFS (pos+1 ,swim_remain-1 ); } } void work () { cin>>n>>m>>k; cin>>river; for (int i=0 ;i<m;i++){ if (BFS (i,k)){ puts ("YES" ); return ; } } puts ("NO" ); return ; } int main () { int test_cases; cin>>test_cases; while (test_cases--) work (); return 0 ; }
E. Novice’s Mistake 题目大意给定 n n n ,求所有的 ( a , b ) (a,b) ( a , b ) 对,满足 a × n − b a\times n-b a × n − b 的结果对应的(非空)字符串刚好和将 n n n 对应字符串复制并拼接 a a a 次,然后裁剪掉长度为 b b b 的后缀得到的字符串一样。
解题思路a , b a,b a , b 的范围 1 0 5 10^5 1 0 5 ,因此不能用最直观的暴力 O ( n 2 ) O(n^2) O ( n 2 ) 解法。
注意到 a × n a\times n a × n 的范围,最多不会超过 n × 1 0 4 n\times 10^4 n × 1 0 4 。又根据“字符串复制拼接”操作,得出 a × n − b a\times n-b a × n − b 对应结果只有那么几种。比如 n = 34 n=34 n = 3 4 时,a × n − b a\times n-b a × n − b 可能取值只有 3 , 34 , 343 , 3434 , 34343 , 343434 3,34,343,3434,34343,343434 3 , 3 4 , 3 4 3 , 3 4 3 4 , 3 4 3 4 3 , 3 4 3 4 3 4 。针对这些值再暴力枚举 a a a 即可。此方法的时间复杂度为 O ( n log n ) 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 #include <bits/stdc++.h> using namespace std;int n;int ground_truth;queue<int >res; void verify (int X) { for (int ai=1 ;ai<=10000 ;ai++){ int b=ai*n-X; if (b<=0 )continue ; if (b>min (10000 ,ai*n))continue ; string str_n=to_string (n); int len_an=ai*str_n.length (); int len_X=to_string (X).length (); if (len_an-b!=len_X)continue ; if (b>=len_an)continue ; res.push (ai); res.push (b); } } void work () { cin>>n; string str_n=to_string (n); int seg=str_n.length (); str_n=str_n+str_n+str_n+str_n+str_n; while (str_n.length ()>seg+4 ){ str_n.erase (str_n.length ()-1 ); } while (str_n.length ()){ verify (stoi (str_n)); str_n.erase (str_n.length ()-1 ); } cout<<res.size ()/2 <<endl; while (!res.empty ()){ int tmp=res.front (); res.pop (); cout<<tmp<<" " ; tmp=res.front (); res.pop (); cout<<tmp<<endl; } return ; } int main () { int test_cases; cin>>test_cases; while (test_cases--) work (); return 0 ; }
F. Valuable Cards 题目大意给定一个数组和一个值 x x x 。目标是将数组划分成最少的段数,使得每段中都不存在任何一个子集,其中所有元素的积为 x x x 。
解题思路贪心。一段一段地找,只要发现有几个元素相乘等于 x x x ,就立马切开。具体实现可以使用 C++ STL 里高效的 set 来存储所有 x x x 除以一些元素的商,一旦新扫描的值匹配上了,就立刻切开。
参考代码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 maxn=1e5 +5 ;int n,x;vector<int >a; vector<int >temp; set<int > divi; void work () { a.clear (); divi.clear (); temp.clear (); cin>>n>>x; for (int i=1 ;i<=n;i++){ int tmp; cin>>tmp; if (x%tmp==0 )a.push_back (tmp); } if (a.size ()==0 ){ puts ("1" ); return ; } int res=1 ,left=0 ,ptr=0 ; divi.insert (x); divi.insert (x/a[ptr]); ptr++; while (ptr<a.size ()){ if (divi.find (a[ptr])!=divi.end ()){ res++; left=ptr; divi.clear (); divi.insert (x); divi.insert (x/a[ptr]); ptr++; continue ; } temp.clear (); for (auto it:divi){ if (it%a[ptr]==0 ){ temp.push_back (it/a[ptr]); } } for (auto it:temp){ divi.insert (it); } ptr++; } cout<<res<<endl; return ; return ; } int main () { int test_cases; cin>>test_cases; while (test_cases--) work (); return 0 ; }
G. Ultra-Meow 题目大意定义 M E X ( a , b ) MEX(a,b) M E X ( a , b ) 函数表示 a a a 集合中未出现的第 k k k 个正整数。求 { 1 , ⋯ , n } \{1,\cdots,n\} { 1 , ⋯ , n } 的所有子集 b b b 的 M E X MEX M E X 值之和 ∑ b ⊆ a M E X ( b , ∣ b ∣ + 1 ) \sum_{b\subseteq a}MEX(b,|b|+1) ∑ b ⊆ a M E X ( b , ∣ b ∣ + 1 ) .
解题思路对于 n n n ,M E X MEX M E X 函数的取值范围是 1 1 1 到 2 n + 1 2n+1 2 n + 1 。对于每个 M E X MEX M E X 函数值,从 0 0 0 到 n n n 枚举子集的大小 s i z e size s i z e 。
对于 M E X MEX M E X 值为 v a l val v a l , 子集大小为 s i z e size s i z e 而言,
1 , ⋯ , v a l − 1 1,\cdots,val-1 1 , ⋯ , v a l − 1 位置上必须刚好有 s i z e size s i z e 个位置不在子集里。那么刚好有 a = ( v a l − 1 ) − s i z e a=(val-1)-size a = ( v a l − 1 ) − s i z e 个位置上的值在子集里。
(如果 v a l val v a l 比 n n n 大,v a l − 1 val-1 v a l − 1 被进一步限制为 n n n )
情况的可能性有 ( min { v a l − 1 , n } a ) \left(\begin{array}{cc}\min\{val-1,n\}\\a\end{array}\right) ( min { v a l − 1 , n } a ) 种。
如果 v a l val v a l 比 n n n 小,那么 v a l + 1 , ⋯ , n val+1,\cdots,n v a l + 1 , ⋯ , n 位置上就刚好有 s i z e − a size-a s i z e − a 个位置上的值在子集里。该情况的可能性有 ( n − v a l s i z e − a ) \left( \begin{array}{cc} n-val\\ size-a \end{array} \right) ( n − v a l s i z e − a ) 种。
因此,M E X MEX M E X 值为 v a l val v a l ,子集大小为 s i z e size s i z e 对应的子集个数为:
( min { v a l − 1 , n } a ) × ( max { 0 , n − v a l } s i z e − a ) \left(\begin{array}{cc}\min\{val-1,n\}\\ a \end{array}\right) \times \left( \begin{array}{cc} \max\{0,n-val\}\\ size-a \end{array} \right) ( min { v a l − 1 , n } a ) × ( max { 0 , n − v a l } s i z e − a )
最终的结果就是对应的 M E X MEX M E X 与之相乘再求和。
参考代码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 #include <bits/stdc++.h> using namespace std;typedef long long ll;const ll mod = 1e9 +7 ;const int maxn = 5010 ;ll C[maxn][maxn]; int n;void prepare () { for (int i=1 ;i<maxn;i++){ C[i-1 ][0 ]=1 ; for (int j=1 ;j<maxn;j++){ C[i][j]=(C[i-1 ][j]+C[i-1 ][j-1 ])%mod; } } } void work () { cin>>n; int res=0 ; for (int sz=0 ;sz<=n;sz++){ for (int mex=1 ;mex<=2 *n+1 ;mex++){ int a=mex-sz-1 ,b=sz-a; if (a>=0 &&b>=0 ){ res=(res+C[min (mex-1 ,n)][a]*C[max (0 ,n-mex)][b]%mod*mex)%mod; } } } cout<<res<<endl; return ; } int main () { prepare (); int test_cases; cin>>test_cases; while (test_cases--) work (); return 0 ; }