A. ABC
题目简述:
给定01字符串S ,判断其是否有长度 >= 2 的回文子串,有则输出YES,无则输出NO。
思路:
对于长度 >= 3 的 S 必定包含长度 >= 2的回文子串,当长度为2或1时注意特判。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve()
{
int n;
string s;
cin >> n >> s;
if (n == 1) {
cout << "YES" << endl;
} else if (n == 2) {
if (s[0] == s[1])
cout << "NO" << endl;
else
cout << "YES" << endl;
} else {
cout << "NO" << endl;
}
}
int main()
{
ios::sync_with_stdio(0);
int t;
cin >> t;
while (t --)
solve();
return 0;
}
B. Roof Construction
题目简述:
给定长度为 n 的排列数a, 权值从 0 到 n - 1, 设该排列数的总花费为 m a x 1 < = i < = n − 1 p i ⨁ p i + 1 max_{1<=i<=n-1} \ p_i \bigoplus p_{i+1} max1<=i<=n−1 pi⨁pi+1 , 求构造一种排列序列使总花费最小。
思路:
显然构造成本为相邻异或的最大值,该最大值由二进制最高位决定,由 n 降序输出直至 log(n), 在从 0 升序输出到 log(n)
代码:
#include <bits/stdc++.h>
using namespace std;
void solve()
{
int a, b;
cin >> a >> b;
// 初始答案设置为 b - a
int ans = b - a;
// 枚举 a1
for (int a1 = a; a1 < b; a1 ++) {
int b1 = 0;
// 确定b1
for (int i = 21; i >= 0; i --) {
if ((b >> i) & 1) {
// b1 二进制高位与 b 相同
b1 ^= (1 << i);
} else if ((a1 >> i) & 1){
// b1 当前仅当a1该二进制位为0,b为1时更改,同时结束循环。
b1 ^= (1 << i);
break;
}
}
ans = min(ans, a1 - a - b + (a1|b1) + 1);
}
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(0);
int t;
cin >> t;
while (t --)
solve();
return 0;
}
C. Strange Test
题目简述:
通过三种操作使 a 与 b 相等, 三种操作
- a += 1
- b += 1
- a = a or b
思路:
该题感性题解即好,首先需要明确的点为或操作仅且操作一次,不使用或时答案为 b - a, 使用时最优解一定需让a加一定次数,b加一定次数(加后设为 a1, b1),或操作后b1(a1|b1 > b1)在加一定次数,答案为(a1 - a) + (b1 - b) + ((a1|b1) - b1) + 1。代码思路为,枚举确定a1,当a1确定以后b1也可以确定(原因在于通过枚举二进制位,确定当且仅当 a1 和 b 的某二进制位分别为 1, 0),将b1变为…1000…,此时答案可记为: ( a 1 − a ) + ( b 1 − b ) + ( ( a 1 ∣ b 1 ) − b 1 + 1 ) (a1-a)+(b1-b)+((a1|b1)-b1 + 1) (a1−a)+(b1−b)+((a1∣b1)−b1+1)
#include <bits/stdc++.h>
using namespace std;
void solve()
{
int a, b;
cin >> a >> b;
// 初始答案设置为 b - a
int ans = b - a;
// 枚举 a1
for (int a1 = a; a1 < b; a1 ++) {
int b1 = 0;
// 确定b1
for (int i = 21; i >= 0; i --) {
if ((b >> i) & 1) {
// b1 二进制高位与 b 相同
b1 ^= (1 << i);
} else if ((a1 >> i) & 1){
// b1 当前仅当a1该二进制位为0,b为1时更改,同时结束循环。
b1 ^= (1 << i);
break;
}
}
ans = min(ans, a1 - a - b + (a1|b1) + 1);
}
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(0);
int t;
cin >> t;
while (t --)
solve();
return 0;
}
D.New Year Concert
题目简述:
给定长度为n的整数序列a, 通过改变任意 a i a_i ai 任意次数使得 g c d ( l , r ) ! = r − l + 1 ( 1 < = l < = r < = n ) gcd(l, r) \ != r - l + 1(1<=l<=r<=n) gcd(l,r) !=r−l+1(1<=l<=r<=n) , 求1到第i段子序列最少需要改变的数的数量。
思路:
首先需要明确该题一定需要线段树维护区间gcd,区间gcd有一个性质为区间越长,gcd越小。首先明确暴力方法,去枚举由左及右枚举右端点,使以该端点为右端点的子区间满足 g c d ( l , r ) ! = r − l + 1 gcd(l,r) != r - l + 1 gcd(l,r)!=r−l+1,显然,方法为将第 a[i] 更改为一个大素数,使任意gcd区间值为1(除去当前端点),更改完成后更新左端点即可。根据之前的gcd性质可推测出可用二分求解,二分区间到右端点的距离,gcd值随着距右端点距离缩短而增大,在二分前注意特判a[i] = 1 情况,必须更新当前值。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 7;
struct Node {
int l, r, gcd;
} tr[N << 2];
int n;
int a[N];
void push_up(int u)
{
tr[u].gcd = __gcd(tr[u << 1].gcd, tr[u << 1 | 1].gcd);
}
void build(int u, int l, int r)
{
if (l == r) {
tr[u] = {l, r, a[l]};
return;
}
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
push_up(u);
}
int query(int u, int L, int R)
{
if (tr[u].l >= L && tr[u].r <= R)
return tr[u].gcd;
int mid = tr[u].l + tr[u].r >> 1, gcd = 0;
if (L <= mid)
gcd = query(u << 1, L, R);
if (R > mid)
gcd = __gcd(gcd, query(u << 1 | 1, L, R));
return gcd;
}
bool lower_find(int l, int r)
{
int m = r;
while (l < r) {
int mid = (l + r) >> 1;
if (query(1, mid, m) < m - mid + 1)
l = mid + 1;
else
r = mid;
}
if (query(1, l, m) == m - l + 1)
return true;
else
return false;
}
int main()
{
ios::sync_with_stdio(0);
cin >> n;
for (int i = 1; i <= n; i ++)
cin >> a[i];
build(1, 1, n);
int pre = 1;
vector<int> ans(n + 1, 0);
for (int i = 1; i <= n; i ++) {
if (a[i] == 1) {
ans[i] = ans[i - 1] + 1;
pre = i + 1;
continue;
}
bool flag = lower_find(pre, i);
if (flag) {
ans[i] = ans[i - 1] + 1;
pre = i + 1;
} else {
ans[i] = ans[i - 1];
}
}
for (int i = 1; i <= n; i ++)
cout << ans[i] << " ";
return 0;
}