【星航计划】2024.11 Div. 3 题解
2024 -- 星航计划 --十一月份 -- 基础算法
每一段连续的 \(1\) 之间是独立的,我们只需要关心一段连续的 1 的结果。可以证明对于一段连续的 \(1\) ,最优策略是将其划分成多个单独的 \(1\) 以及可能余下的连续两个 \(1\)。
- 对于 \(k\) 个连续的 \(1\) ,如果 \(k\) 是奇数,最优策略是将其划分为 \(\frac{k+1}{2}\) 个单独的 \(1\) ,答案为 \(\frac{k+1}{2}\) ;
- 否则最优策略是将其划分为 \(\frac{k}{2}-1\) 个单独的 \(1\) 和两个连续的 \(1\) ,答案为与 \(\frac{k}{2}-1+\sqrt{2}\) 。
时间复杂度 \(\mathcal{O}(|S|)\) 。
#include <bits/stdc++.h>
#define int long long
using ll = long long;
using pll = std::pair<int, int>;
using namespace std;
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
string s;
cin >> s;
int n = s.size();
s = ' ' + s + '0';
vector<int> per(n + 3);
vector<int> k;
double ans = 0;
for (int i = 1; i <= n + 1; ++i){
if (s[i] == '1')
per[i] = per[i - 1] + 1;
else
if (per[i - 1])
k.push_back(per[i - 1]);
}
for (int i : k){
while (i > 2){
i -= 2;
ans += 1;
}
ans += sqrt(double(i));
}
cout << fixed << setprecision(13) << ans;
return 0;
}
矩形的边无凹凸,所以四角只能使用 \(a\) 块,四边只能使用 \(b\) 、 \(c\) 块,中间只能使用 \(d\) 块。发现 \(b\), \(c\) 块必须相邻使用, \(a\) 块相邻的不能同时 \(b\) 或同时 \(c\) 。从顺时针角度考虑, \(b\), \(c\) 交替使用,因此 \(b\), \(c\) 使用个数一定相同。其余中间总是可以使用 \(d\) 填满。因此可以枚举矩形的长,计算在该长度下,矩形最大的宽度。时间复杂度 \(O(Td)\) 。实际上,可以直接枚举矩形的长与宽,因为 \(d \leq 10^{3}\) ,根据整除等知识,可知合法的长宽数量级别为 \(\mathcal{O}(d \sqrt{d})\) 。时间复杂度为 \(\mathcal{O}(T d \sqrt{d})\) 。
#include <bits/stdc++.h>
using namespace std;
int T, a, b, c, d;
int main() {
cin >> T;
while (T--) {
cin >> a >> b >> c >> d;
if (a < 4) {
cout << 0 << endl;
continue;
}
if (b > c)
b = c;
int ans = 0;
for (int i = 0; i <= b; i++) {
int temp = INT_MAX;
if (i != 0)
temp = d / i;
int t = min(temp, b - i);
ans = max(ans, 4 + i * 2 + t * 2 + i * t);
}
cout << ans << endl;
}
return 0;
}
由于新插入的整数只能插入到相邻整数之间,因此新插入的整数不会对除这两个整数之外的其他整数产生影响,所以接下来我们对每两个相邻整数分别考虑考虑原序列中两相邻整数 \(x\),\(y\) ,利用异或可以将其无限扩展。扩展后如下所示\(x,y,x\oplus y,x,y,x\oplus y\) 因此,可以构造出无限个 \(x\) 与 \(y\) 相邻的情况。
对于 \(x,y\) 二进制下的每一位,只有以下几种可能:
- 均为 \(0\)
- 均为 \(1\)
- \(x\) 为 \(0\) , \(y\) 为 \(1\)
- \(x\) 为 \(1\) , \(y\) 为 \(0\)
注意到对于满足第一种情况的位,不管怎么操作,得到的数都永远为 \(0\)。
剩下的三种情况,每种其实都可以取到 \(0,1\),共 \(2^3=8\) 种情况,以下给出构造
\(x,y,x\&y,x\oplus y,x|y,x\&(x\oplus y),y\&(x\oplus y),0\)
加入 set
里去重即可。
#include <bits/stdc++.h>
using namespace std;
set<int> st;
int arr[100005];
int n;
int main() {
cin >> n,arr[0];
st.emplace(0);
for (int i = 1; i < n; i++) {
cin >> arr[i];
int a = arr[i - 1],b = arr[i];
st.emplace(a);
st.emplace(b);
st.emplace(a | b);
st.emplace(a & b);
st.emplace(a ^ b);
st.emplace((a ^ b) & a);
st.emplace((a ^ b) & b);
}
cout << st.size();
return 0;
}
只考虑任意相邻的两项什么时候满足 \(a_i \oplus x \le a_{i+1} \oplus x\),容易发现充要条件是从高到低找到 \(a_i\) 和 \(a_{i+1}\) 第一个二进制下不同的位置 \(pos\),若 \(a_i\) 在 \(pos\) 这一位为 \(0\),则 \(x\) 这一位必须为 ,否则 \(x\) 这一位必须为 \(1\)。
如果存在一位,既要求放 \(0\) 又要求放 \(1\),那么答案一定为无解。否则,把所有有要求的位按照要求放置,无要求的二进制位填入 \(0\) 即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 7, M = 65;
int n, g[M][2];
long long k, f[N];
void solve()
{
scanf("%d", &n);
for (int i = 60; i >= 0; i--)
g[i][0] = g[i][1] = 1;
for (int i = 1; i <= n; i++)
scanf("%lld", &f[i]);
for (int i = 1; i < n; i++)
for (int j = 60; j >= 0; j--)
if (((f[i] >> j) & 1) != ((f[i + 1] >> j) & 1))
{
if ((f[i] >> j) & 1)
g[j][0] = 0;
else
g[j][1] = 0;
break;
}
long long ans = 1, sum = 0;
for (int i = 60; i >= 0; i--)
{
// cout<<g[i][0]<<g[i][1]<<endl;
if (!g[i][0] && !g[i][1])
{
sum = -1;
break;
}
if (!g[i][0])
sum |= (1ll << i);
}
cout << sum << '\n';
}
int main()
{
int T;
cin >> T;
while (T--)
solve();
return 0;
}