题目链接https://codeforces.com/contest/1996
A. Legs 题意 一只鸡有两条腿,一只牛有四条腿,一共有$n$($n$是偶数)条腿,问最少多少只动物。
思路 全是牛的时候动物数量最多, 所以$ans = \lceil \frac{n}{4} \rceil$
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #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; cin>>n; cout<<(n+3 )/4 <<endl; } signed main () { int T = 1 ; cin>>T; while (T--){ solve (); } return 0 ; }
B. Scale 题意 有一个01数组,请你缩小k倍,然后输出它。
例如:
1 2 3 4 5 6 7 8 1 6 3 000111 000111 000111 111000 111000 111000
缩小完变为
思路 循环输出,每次跳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 #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;char mp[1005 ][1005 ];void solve () { int n,k; cin>>n>>k; rep (i,1 ,n){ rep (j,1 ,n) cin>>mp[i][j]; } for (int i =1 ;i<=n;i+=k){ for (int j = 1 ;j<=n;j+=k){ cout<<mp[i][j]; } puts ("" ); } } signed main () { int T = 1 ; cin>>T; while (T--){ solve (); } return 0 ; }
C. Sort 题意 给你两个字符串 $a,b$ , 进行$q$次询问,每次询问$l,r$ 。 对于每次询问,你可以进行一些次数操作,每次可以使得一个字符变为任意另一个字符, 问最少多少次操作,能够使得 $sorted(a[l..r]) = sorted(b[l..r])$
注意:每次询问的修改操作不影响之后的询问。
思路 因为我们会对这两个子串进行排序操作,所以我们就只需要考虑子串中每个字符出现的次数即可。
我们定义两个子串的差异度为$\sum_{ch = a}^z |cnta[ch]-cntb[ch]|$ ,即每种字符在两个子串中数量的差的总和。
我们每次操作可以将一个字符变为另一个字符,因此会使得二者的差异度减少2 。
所以我们总共的操作次数就是差异度除以2。 即$\frac{\sum_{ch = a}^z |cnta[ch]-cntb[ch]|}{2}$
于是我们需要解决的问题变为:如何更快地求出某一子串中所含有的字符数量: 前缀和 ,使用一个sum[N][26]
数组来记录每个字符出现的次数。于是对于某一子串$a[l..r]$ , 字符$ch$ , 出现的次数为sum[r][ch-'a'] - sum[l-1][ch-'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 37 38 39 #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 cnt1[200005 ][27 ];int cnt2[200005 ][27 ];void solve () { int n,q; cin>>n>>q; string a,b; cin>>a>>b; a = ' ' + a; b = ' ' + b; for (int i = 1 ;i<=n;i++){ for (int j = 0 ;j<26 ;j++){ cnt1[i][j] = cnt1[i-1 ][j] + (a[i] == 'a' + j); cnt2[i][j] = cnt2[i-1 ][j] + (b[i] == 'a' + j); } } while (q--){ int l, r; cin>>l>>r; int ans = 0 ; for (int j = 0 ;j<26 ;j++){ ans += abs ((cnt1[r][j] - cnt1[l-1 ][j])-(cnt2[r][j] - cnt2[l-1 ][j])); } cout<<ans/2 <<'\n' ; } } signed main () { int T = 1 ; cin>>T; while (T--){ solve (); } return 0 ; }
D. Fun 题意 给定两个数$n,x$ , 求满足$a+b+c\le x , ab + ac + bc \le n$ 的三元组$(a,b,c)$ 的个数。 $1\le n ,x \le 10^6$
注意:三元组是有序的,即$(1,1,2),(2,1,1)$ 视为不同的三元组
思路 如果暴力枚举,我们需要三重循环分别枚举$a,b,c$ ,他们的取值为 $1\le a,b,c \le x-2$ ,此时的复杂度为$O(x^3)$ ,显然是不行的。考虑根据两个条件节时, 我们枚举最外层变量为a, 即在a确定之后:
根据条件1: 一定有$b \le x - a - c \le x - a - 1$ ,于是b只需要枚举到$x-a-1$即可。
根据条件2:一定有$a * b < n$ , 即b最大为$\lfloor \frac{n}{a} \rfloor$
二者取min,得到b的最大值。可以得出,此时枚举a和b的复杂度大概为$nlnn$ , 那么我们需要$O(1)$ 求出c的取值范围,在确定完$a,b$ 的值后,可以得出$ 1\le c \le min(x - a -b,\frac{ n - ab}{a+b})$ , 而对于每个c,都会对答案贡献1,因此在确定完a和b后, 我们直接令答案加上$ min(x - a -b,\frac{ n - a b}{a+b})$ 即可。总体时间复杂度即为枚举a和b的复杂度$O(nlnn)$。
代码 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 #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,x; cin>>n>>x; int ans =0 ; rep (a,1 ,x){ rep (b,1 ,x){ if (a+b>=x) break ; if (a*b>=n) break ; int nn = n - a * b; int c1 = (n-a*b) / (a+b); int c2 = x - a - b; ans += min (c1,c2); } } cout<<ans<<endl; } signed main () { int T = 1 ; cin>>T; while (T--){ solve (); } return 0 ; }
E. Decode 题意 给一个01串S,求对于每一对$(l,r), (1\le l\le r\le n)$ ,计算$(x,y) ,(l\le x\le y \le r)$ 这样的整数对的个数,要求$S[x..y]$ 中0 和1的数量相同。
思路 我们考虑逆向思维,找出每一个满足$f_0(S[x..y]) = f_1(S[x..y])$ 的$(x,y)$对, 计算每个对的贡献(即为 在多少个子串$S[l..r]$ 中),将其加和即可。 如果我们确定一个$(x,y)$ 那么很容易算出, 它的贡献为$x * (n-y+1)$ 。
那么如何确定这些$(x,y)$ 对呢,我们考虑前缀和,将字符0
的贡献为-1, 字符1的贡献为1
,那么$f_0(S[x..y]) = f_1(S[x..y])$ 就等价于$sum[y] = sum[x-1]$ , 我们边枚举y边对这么一个sum数组进行桶计数。于是$cnt[sum[y]]$即表示在y之前,有多少个$x$满足$sum[x-1] = sum[y]$ ,但是我们记录数量是不够的,我们还要考虑x的位置,即对于每个y,贡献为$x_1(n-y+1) + x_2 (n-y+1)+…+x_k(n-y+1) = (x_1+x_2+…+x_k) (n-y+1)$ ,于是我们的桶数组每次应该令$cnt[sum[x]] += i+1$ , 即记录为每个满足条件的$x$的和。 (注意每次加到$cnt$数组中的是$i+1$,不是$i$ ,因为$i = x-1$)
注意$sum[x]$ 可能是负数,我们进行桶计数是要进行偏移$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 #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;const int mod = 1e9 +7 ;void solve () { string str; cin>>str; int n = str.size ();str = ' ' + str; vector<int > cnt (2 * n + 5 ) ; vector<int > sum (n+5 ) ; cnt[0 +n] = 1 ; int ans = 0 ; for (int i = 1 ;i <= n;i++){ int y = i; sum[y] = sum[y-1 ]; if (str[y] == '0' ) sum[y]--; else sum[y] ++; ans = (ans + cnt[sum[y]+n] * (n-y+1 )%mod)%mod; cnt[sum[y] + n] += i+1 ; } cout<<ans<<endl; } signed main () { int T = 1 ; cin>>T; while (T--){ solve (); } return 0 ; }
F. Bomb 题意 给两个长度为$n$的数组$a,b$ , 你至多进行$k(1\le k \le 10^9)$次操作,每次操作如下: 选择一个数$i$ , 获得$a[i]$ 枚金币,并令$a[i] = max(0,a[i]-b[i])$
问最多能获得多少金币
思路 我们先考虑$O(k)$ 的朴素解法: 每次选择一个最大的$a[i]$ ,选择完后使用堆(优先队列)更新最大值,重复选择k次即可。
但是上述代码会导致超时(因为k太大了), 我们考虑二分搜索:
我们设$f(x)$表示令所有的$a[i]$ 都减至$a[i] < x$ ,所需要的操作次数。 显而易见,$f(x)$ 是单调递减的。因此满足二分性。
我们找到这样一个$l$ , 使得$f(l) > k , f(l+1) \le k$ , 这样我们先令所有的$a[i]$ 都减至小于 $l$ ,此时相比k,会多进行$f(l)-k$ 次操作, 因此会导致多获得一些金币,这里每次操作会使我们额外获得$l$的贡献,令最终的金币数量减去$(f(l)-k)*l$ 即为最终答案。
代码 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 #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 n,k;int a[200005 ];int b[200005 ];bool check (int x) { int sum = 0 ; for (int i = 1 ;i<=n;i++){ if (a[i]>=x){ sum += (a[i]-x)/b[i] + 1 ; } } return sum <= k; } void solve () { cin>>n>>k; rep (i,1 ,n) cin>>a[i]; rep (i,1 ,n) cin>>b[i]; int l =0 ; int r =1e9 +5 ; while (l<r){ int mid = (l+r+1 )>>1 ; if (check (mid)){ r = mid-1 ; }else { l = mid; } } int ans = 0 ; int s = 0 ; for (int i = 1 ;i<=n;i++){ if (a[i]>=l){ int m = (a[i] - l)/b[i] + 1 ; ans += (a[i] + a[i]-(m-1 )*b[i])*m/2 ; s += m; } } ans -= 1LL * l * (s - k); cout<<ans<<endl; } signed main () { int T = 1 ; cin>>T; while (T--){ solve (); } return 0 ; }