比赛链接https://codeforces.com/contest/1995
A. Diagonals 题意 给定一个$n*n$ 的矩阵, 定义对角线为$i+j$ 相同的值,其中$i$为行号,$j$为列号 。
给你k个棋子要放在这么一个$n*n$ 的矩阵中, 问最少需要占据多少个对角线。
思路 对角线的长度为 $n,n-1,n-1,n-2,n-2,…,1,1$ 。 我们按照对角线的长度从大到小按顺序填充棋子即可。
代码 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 #include <bits/stdc++.h> using namespace std;#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define int long long #define rep(i,l,r) for(int i = l;i<=r;i++) #define per(i,r,l) for(int i = r;i>=l;i--) const int INF = 0x3f3f3f3f3f3f3f3f ;typedef pair<int ,int > PII;void solve () { int n,k; cin>>n>>k; if (k == 0 ) { cout<<0 <<endl; return ; } int cnt =1 ; k -= n; int t = n-1 ; int f = 0 ; while (k>0 ){ k -= t; cnt++; if (f) t--; f ^= 1 ; } cout<<cnt<<endl; } signed main () { int T = 1 ; cin>>T; while (T--){ solve (); } return 0 ; }
B. Bouquet 由于简单版本和困难版本差距不大,这边直接上hard vision
题意 有一个花店, 花店里有一些花,我们按照花所含有的花瓣数量来对花分类。你总共有x金币。
你可以花费k个金币购买一个拥有k个花瓣的花,并且你购买的花中,任意两朵花的花瓣数量相差不大于$1$ 。 问你最多能获得多少花瓣。
思路 我们使用map来记录每个花瓣的花有多少朵 , 显而易见, 若购买k花瓣的花,那么便只能再购买k+1花瓣的花
。所以我们枚举k为花店中每一种花的花瓣数量, 在每次枚举中,我们只考虑购买 k 和k+1的情况,最终在枚举的每种情况下取MAX即可。我们按照如下贪心策略来购买花,就可以保证买到的花瓣数量尽可能多。
尽可能多地购买k花瓣的花(直到没钱买或者把所有的花都买完了)
尽可能多地购买k+1花瓣的花(直到没钱买或者把所有的花都买完了)
尽可能多地将一些k花瓣的花换成k+1花瓣的花(每换一个花需要额外1金币, 并且要求有足够的k+1花瓣的花)
按照如上思路就可以得到“只够买k和k+1花瓣的花的情况下, 最多购买多少花瓣” ,将所有k的取值对应的答案取最大值就可以得到最终答案。
代码 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 #include <bits/stdc++.h> using namespace std;#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define int long long #define rep(i,l,r) for(int i = l;i<=r;i++) #define per(i,r,l) for(int i = r;i>=l;i--) const int INF = 0x3f3f3f3f3f3f3f3f ;typedef pair<int ,int > PII;void solve () { int n,m; cin>>n>>m; map<int ,int > mp; vector<int > a (n+1 ) ,b (n+1 ) ; rep (i,1 ,n){ cin>>a[i]; } rep (i,1 ,n){ cin>>b[i]; } rep (i,1 ,n) mp[a[i]] = b[i]; int ans = 0 ; for (auto [x,y] : mp){ if (y==0 ) continue ; int sum = 0 ; int mm = m; int t1 = min (y,mm/x); mm -= t1 * x; int y2 = mp[x+1 ]; int t2 = min (y2,mm/(x+1 )); mm -= t2 * (x+1 ); y2 -= t2; int t3 = min (mm,min (y2,t1)); sum = t1 * x + t2 * (x+1 ) + t3; ans = max (ans,sum); } cout<<ans<<'\n' ; } signed main () { int T = 1 ; cin>>T; while (T--){ solve (); } return 0 ; }
C. Squaring 题意 给一个长度为$n$的数组$a_1,a_2,..,a_n$ , 每次操作你可以选择一个数$i$, 令$a_i = a_i ^2$ ,请问最少需要多少次操作,能让数组a变为单调不减数列。若无论如何都无法实现,那么输出-1
思路 可以很容易想到,我们从左到右遍历数组,不断检查每一个数是否大于等于上一个数,若出现$ai < a {i-1}$ 就对$ai$进行操作,直到满足$a {i} \ge a_{i-1}$ 。
特判:只有当存在$ai = 1 ,a {i-1} > 1$ 时, 无论如何都无法令$ a_{i-1}\le a_i$ ,此时要输出-1
。
本题的难点在于在对一个数进行操作时,我们很容易就会发生超出longlong范围的情况,因此我们不能直接比较两个数的大小,而应该将他们作为$a^p ,b^q$ 的形式,直接比较二者的大小。
错解:
起初我很容易想到了一个”解法“, 即若判断 $a^p, b^q$ 的大小关系, 可以比较 $a^{\frac{1}{q}} $ 和$b^{\frac{1}{p}}$ 的大小关系, 于是便成功WA5了。。。
正解:
本题我们要用到每次操作都是平方的特性,即$a^p ,b^q$中的$p,q$ 都是2的次幂 , 假设 $ a{i-1}^p > a {i}$ , 我们可以先令二者的幂相同,即设$c$ ,满足$c^p = a{i}$ , 于是为了令$a^p {i-1} \le ai^q =(c^p)^q = (c^q)^p$ , 于是我们只需要找到一个$q$ ,使得恰有$c^q \ge a {i-1}$ 即可, 也即$ai ^ {\frac{q}{p}} \ge a {i-1}$ , 又因为我们每次进行操作为乘方操作, 所以有$p,q$ 都是2的次幂 , 设$p = 2^x,q = 2^y$ , 那么$a_i^{\frac{q}{p}} = a_i^{2^{y-x}}$ , 此处的$y-x$ 即为我们要对$a_i$ 进行的操作次数。
那么会出现下面两种情况:
$ai \le a {i-1}$ ,那么我们不断对$ai$ 进行乘二操作, 直到进行完$cnt$ 次操作后,$a_i \ge a {i-1}$ ,此时对$a_i$ 进行的总次数即为$q = p + cnt$
$ai > a {i-1}$ ,那么我们不断对$ai$ 进行除以二操作, 直到进行完$cnt$ 次操作后,恰好$a_i \ge a {i-1}$ , 此时$a_i$ 进行的总次数即为$q = p - cnt$
代码 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 #include <bits/stdc++.h> using namespace std;#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define int long long #define rep(i,l,r) for(int i = l;i<=r;i++) #define per(i,r,l) for(int i = r;i>=l;i--) #define endl '\n' const int INF = 0x3f3f3f3f3f3f3f3f ;typedef pair<int ,int > PII;struct node { int x; int ope = 0 ; }; void solve () { int n; cin>>n; vector<int > a (n+5 ) ; int ans = 0 ; vector<node> b (n+5 ) ; for (int i = 1 ;i<=n;i++){ cin>>a[i]; } for (int i = 2 ;i<=n;i++){ if (a[i] < a[i-1 ] && a[i] == 1 ){ cout<<-1 <<'\n' ; return ; } } for (int i = 1 ;i<=n;i++){ b[i] = {a[i],0 }; } for (int i = 2 ;i<=n;i++){ int x1 = a[i-1 ]; int q1 = b[i-1 ].ope; int x2 = a[i]; int q2 = b[i].ope; if (x1 > x2){ int xx = x2; int t = 0 ; while (xx < x1){ xx *= xx; t++; } b[i].ope = b[i-1 ].ope + t; }else if (x1 == x2){ b[i].ope = b[i-1 ].ope; }else { if (b[i-1 ].x == 1 ) { b[i].ope = 0 ; continue ; } int t = 0 ; int xx = x1; while (xx < x2){ xx *= xx; t++; } if (xx>x2) t--; b[i].ope = max (0LL ,b[i-1 ].ope-t); } ans += b[i].ope; } cout<<ans<<'\n' ; } signed main () { int T = 1 ; cin>>T; while (T--){ solve (); } return 0 ; }
D. Cases 题意 给你一个长度为$n$的只含有前$c$种字母的字符串$S$,你可以将这个字符串切割为一些子串,并使得每一子串的长度都不大于$k$ , 定义切割后的字符串的价值为选取每一个子串的最后一个字符,将其放入集合set中,最终set的大小
, 即所有子串最后一个字符的种类数量。
问给定$n,c,k,S$ ,最小价值为多少。
思路 题意可以理解为, 我们尽可能少地选择一些字母, 要求相邻的被选择的字母之间距离不大于k。
也即: 任选一个长度为k的子串,其中必须包含至少一个我们所选择的字母。
我们判断一个长度为k的子串中有没有某一个字符,可以使用前缀和来判断,我们用$sum[i][j]$ 表示前i个字符中,有多少个字符j
if(sum[i+k][j] - sum[i][j]==0)
我们提取所有的长度为k的子串,将他们没出现过的字母状态进行二进制表示后存储起来,表示“只选择这些字母,会出现错误”。设对应的二进制码为msk,那么就令bad[msk] = 1
.
较容易得知,如果我们在选择一个组合之后,会发生错误,那么我们再从中去掉一些后,同样会出现错误。(如若选择AC
两种字母会出现相邻的被选择的字母之间距离大于k
,那么如果我们只选择A
或者C
,就一定也会导致相邻的被选择的字母之间距离大于k
例如样例:
我们提取出长度为2的子串后,得到AB
, BC
, CA
, AB
,BC
, 取反后得到C
,A
,B
,C
,A
, 即如果选择其中一种组合或其子集(A或B或C或空) ,会出现错误。
在处理完bad数组后, 我们选出含有1最少的i,并且bad[i] == 0
, 于是$i$中1的数量即为答案。
代码 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 #include <bits/stdc++.h> using namespace std;#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define int long long #define rep(i,l,r) for(int i = l;i<=r;i++) #define per(i,r,l) for(int i = r;i>=l;i--) const int INF = 0x3f3f3f3f3f3f3f3f ;typedef pair<int ,int > PII;int sum[300005 ][20 ];void solve () { int n,c,k; cin>>n>>c>>k; string str; cin>>str;str = " " + str; for (int j = 0 ;j<c;j++) sum[0 ][j] = 0 ; for (int i = 1 ;i<=n;i++){ for (int j = 0 ;j<c;j++){ sum[i][j] = sum[i-1 ][j] + (str[i] == j + 'A' ); } } vector<bool > bad ((1 <<c)) ; for (int i= 0 ;i<=n-k;i++){ int msk = 0 ; for (int j = 0 ;j<c;j++){ if (sum[i+k][j] == sum[i][j] ){ msk |= (1 <<j); } } bad[msk] = 1 ; } bad[(1 << (str.back () - 'A' ))^((1 <<c)-1 )] = 1 ; for (int i =(1 <<c)-1 ;i>=0 ;i--){ for (int j=0 ;j<c;j++){ bad[i] = bad[i] | bad[i | (1 <<j)]; } } int res = INF; for (int i = 0 ;i<(1 <<c);i++){ if (!bad[i]){ int cnt = __builtin_popcount(i); res = min (res,cnt); } } cout<<res<<endl; } signed main () { int T = 1 ; cin>>T; while (T--){ solve (); } return 0 ; }