文章目录
刷题之Codeforces Round #769 (Div. 2)
1632A. ABC
-
题目:判断 01 字符串中是否包含回文子串
-
思路:
- ∣ s ∣ ≥ 3 |s|\ge 3 ∣s∣≥3 :一定不满足
- ∣ s ∣ = 2 |s|=2 ∣s∣=2: s s s 为 00 00 00 或 11 11 11 时是回文
#include<bits/stdc++.h> using namespace std; #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define per(i, a, b) for(int i = (a); i >= (b); i--) #define ll long long #define db double #define VI vector<int> #define PII pair<int, int> const db Pi = 3.141592653589793; const int INF = 0x7fffffff; const int N = 1e5 + 5; const db eps = 1e-10; int cas, n; string s; int main(){ cin >> cas; while(cas--){ cin >> n >> s; if(n >= 3 || (n == 2 && s[0] == s[1])) puts("NO"); else puts("YES"); } }
1632B. Roof Construction
-
题目:给 0 ∼ n − 1 0\sim n-1 0∼n−1 的一种排列,使得相邻两数的异或和的最大值 ( max 1 ≤ i ≤ n − 1 p i ⊕ p i + 1 ) \left( \max\limits_{1\le i\le n - 1} p_i\oplus p_{i+1} \right) (1≤i≤n−1maxpi⊕pi+1) 最小
-
类型:构造题
-
思路:将 0 ∼ n − 1 0\sim n - 1 0∼n−1 二进制展开后发现, n − 1 n-1 n−1 内的二进制最高位为 1 1 1 与一个最高位为 0 0 0 的数字异或后,最高位一定为 1 1 1,且这个异或后的 1 1 1 是不可避免的。于是我们可以想办法使 max 1 ≤ i ≤ n − 1 p i ⊕ p i + 1 = \max\limits_{1\le i\le n - 1} p_i\oplus p_{i+1}= 1≤i≤n−1maxpi⊕pi+1=
1 << (int)log2(n - 1)
,即最高位的 1 1 1- 只需要将所有最高位为 1 1 1 的相邻,所有最高位为 0 0 0 的相邻。只在需要保证交界处的异或和只在最高位为 1 1 1,且在其他后面的位置均为 0 0 0,于是想到用 0 0 0 调整。
#include<bits/stdc++.h> using namespace std; #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define per(i, a, b) for(int i = (a); i >= (b); i--) #define ll long long #define db double #define VI vector<int> #define PII pair<int, int> const db Pi = 3.141592653589793; const int INF = 0x7fffffff; const int N = 1e5 + 5; const db eps = 1e-10; int cas, n, a; int main(){ cin >> cas; while(cas--){ cin >> n; a = (1 << (int)log2(n - 1)); rep(i, 1, a - 1) printf("%d ", i); printf("%d ", 0); rep(i, a, n - 1) printf("%d ", i); cout << endl; } } /* 4 2 3 5 10 */
1632C. Strange Test
-
题目:给两个数 a , b ( 1 ≤ a < b ≤ 1 0 6 ) a,b\;(1\le a<b\le 10^6) a,b(1≤a<b≤106) 和三个操作
a++
,b++
,a |= b
,求最少操作次数使得 a = b a=b a=b -
类型:位运算,构造题
-
思路:
-
结论一:或运算操作最多只能用一次。
证明:由于当 a > b a>b a>b 后只能通过
b++
使得 a = b a=b a=b。且a |= b
后, a a a 不会减小且 a ≥ b a\ge b a≥b。这时只能进行b++
操作来满足条件。故为使操作次数最少,即使 a − b a-b a−b 最小,不可再进行a |= b
操作。 -
考虑 a , b a,b a,b 只经过
a++
和b++
操作后,变为 a ′ , b ′ a',b' a′,b′ 再进行a |= b
操作和b++
操作。一共需要的次数为
( a ′ − a ) + ( b ′ − b ) + 1 + ( a ′ ∣ b ′ − b ′ ) = ( a ′ + a ′ ∣ b ′ ) + ( 1 − a − b ) (a'-a)+(b'-b)+1 + (a'|b'-b')=(a'+a'|b')+(1-a-b) (a′−a)+(b′−b)+1+(a′∣b′−b′)=(a′+a′∣b′)+(1−a−b)
只需使 a ′ + a ′ ∣ b ′ a'+a'|b' a′+a′∣b′ 最小。由于 a ′ ∈ [ a , b ] a'\in [a,b] a′∈[a,b],因此可暴力枚举 a ′ a' a′ 来寻找满足的 b ′ b' b′ -
结论二:当
a |= b
操作前,可使 a , b a,b a,b 中一个不变,此时不影响结果。证明:真没想出来,若有大佬可证明,请务必评论留言。其实这个结论不用也可做出,代码见此博客
#include<bits/stdc++.h> using namespace std; #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define per(i, a, b) for(int i = (a); i >= (b); i--) #define ll long long #define db double #define VI vector<int> #define PII pair<int, int> const db Pi = 3.141592653589793; const int INF = 0x7fffffff; const int N = 1e5 + 5; const db eps = 1e-10; int cas, a, b, ans; int main(){ cin >> cas; while(cas--){ cin >> a >> b; ans = b - a; rep(i, a, b) //只a++ if((i | b) == b) ans = min(ans, i - a + 1); rep(i, b, 5 * b) //只b++ if((i | a) == i) ans = min(ans, i - b + 1); cout << ans << endl; } } /* 5 1 3 5 8 2 5 3 19 56678 164422 */
-
1632D. New Year Concert
-
题目:对序列 { a } i = 1 n \{a\}_{i=1}^n {a}i=1n 的前缀 { a } i = 1 k \{a\}_{i=1}^k {a}i=1k 求最少修改 a i a_i ai 的数量 f ( { a } i = 1 k ) f\left(\{a\}_{i=1}^k \right) f({a}i=1k) ,使得 { a } i = 1 k \{a\}_{i=1}^k {a}i=1k 中任一连续子区间均满足 gcd ( a l , a l + 1 , . . . , a r ) ≠ r − l + 1 \gcd(a_l,a_{l+1},...,a_r)\ne r-l+1 gcd(al,al+1,...,ar)=r−l+1
-
类型:ST表,贪心
-
思路:
- 首先发现若 gcd ( a l , a l + 1 , . . . , a r ) = r − l + 1 \gcd(a_l,a_{l+1},...,a_r)= r-l+1 gcd(al,al+1,...,ar)=r−l+1 只需将 { a } i = l r \{a\}_{i=l}^r {a}i=lr 内某一个数改为一个未出现的超大素数,即可使得 gcd ( a l , a l + 1 , . . . , a r ) = 1 \gcd(a_l,a_{l+1},...,a_r)=1 gcd(al,al+1,...,ar)=1 满足条件
- 假设固定 a l a_l al 改变 a r a_r ar 发现:当 r ↑ r\uparrow r↑ 时, gcd ( a l , a l + 1 , . . . , a r ) ↓ \gcd(a_l,a_{l+1},...,a_r)\downarrow gcd(al,al+1,...,ar)↓, r − l + 1 ↑ r-l+1\uparrow r−l+1↑. 故在固定 a l a_l al 时最多只存在一个 a r a_r ar,使得 gcd ( a l , a l + 1 , . . . , a r ) = r − l + 1 \gcd(a_l,a_{l+1},...,a_r)= r-l+1 gcd(al,al+1,...,ar)=r−l+1。因此整个序列 { a } i = 1 n \{a\}_{i=1}^n {a}i=1n 中最多有 n n n 个不满足条件的子区间
- 步骤:
- 利用 ST表 预处理可 O ( 1 ) O(1) O(1) 求出任意连续子区间的 gcd \gcd gcd
- 从 1 ∼ n 1\sim n 1∼n 遍历左端点 a l a_l al,二分右端点寻找使得 gcd ( a l , a l + 1 , . . . , a r ) = r − l + 1 \gcd(a_l,a_{l+1},...,a_r)= r-l+1 gcd(al,al+1,...,ar)=r−l+1 的右端点(若有)
- 存下所有不满足条件的子区间,贪心寻找至少修改数字的数量:按照右端点排序,每次修改右端点 a n o w a_{now} anow (使右端点为一个超大素数),并删除所有包含 a n o w a_{now} anow 的子区间,最后统计答案即可。
//st表, greedy #include<bits/stdc++.h> using namespace std; #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define per(i, a, b) for(int i = (a); i >= (b); i--) #define ll long long #define db double #define VI vector<int> #define PII pair<int, int> const db Pi = 3.141592653589793; const int INF = 0x7fffffff; const int N = 2e5 + 5; const db eps = 1e-10; int n, modify[N], cnt, before, ans; int a[N], st[N][30]; int gcd(int a, int b){ return (b ? gcd(b, a % b) : a); } struct AC{ int l, r; }b[N]; bool cmp(AC a, AC b){ if(a.r == b.r) return a.l < b.l; return a.r < b.r; } int check(int l, int r){ int k = (int)log2(r - l + 1); return gcd(st[l][k], st[r - (1 << k) + 1][k]); } int find(int left){ if(a[left] == 1) return left; int l = left, r = n + 1, mid; int ok = 0, GCD; while(l + 1 < r){ mid = (l + r) / 2; GCD = check(left, mid); if(GCD == mid - left + 1){ ok = 1; break; } if(GCD < mid - left + 1) r = mid; else l = mid; } if(!ok && r - l == 1) if(gcd(a[l], a[r]) == 2) ok = 1, mid = r; return ok ? mid : 0; } int main(){ cin >> n; rep(i, 1, n) cin >> a[i]; rep(j, 0, (int)log2(n) + 1){ //st表预处理区间gcd rep(i, 1, n + 1 - (1 << j)){ if(j == 0) st[i][j] = a[i]; else st[i][j] = gcd(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]); } } cnt = 0; rep(i, 1, n){ //固定左端点 int tmp = find(i); if(tmp) b[++cnt] = AC{i, tmp}; } //贪心修改 sort(b + 1, b + 1 + cnt, cmp); before = 0; memset(modify, 0, sizeof(modify)); rep(i, 1, cnt){ if(b[i].l > before){ modify[b[i].r] = 1; before = b[i].r; } } //统计前缀答案 ans = 0; rep(i, 1, n){ ans += modify[i]; printf("%d ", ans); } } /* 7 2 12 4 8 18 3 6 */