题目链接
这题出思路还挺快的,就是考虑二进制每一位的贡献,可惜写了个假的贪心
正确贪心是,考虑两种情况,第一种情况是当处理结束时,a ^ x比b ^ x大,另一种情况是a ^ x比b ^ x小
当结束时前面比后面大,遍历每一位中 a是1 b是0 的位置就要尽可能让 a 变成 0 b变成 1,这样需要的代价是2的位数的次方,可以减少的差值是两倍的次方
当结束时前面比后面小,遍历每一位中 a是0 b是1 的位置就要尽可能让 a 变成 1 b变成 0,这样需要的代价是2的位数的次方,可以减少的差值是两倍的次方
有个需要注意的点是,要单独处理一下ab第一个不同的数,如果结束a大于b,那第一个不同的数一定要是a为1 b为0,如果不是就要调整成这样,调整不了就不考虑这种情况,a小于b也是类似的
下面是我很啰嗦的代码,可以把后面类似的两部分写成一个函数
#include
using namespace std;
#define int long long
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
const int N = 1000010;
int cf[63];
void init()
{
cf[0] = 1;
for (int i = 1; i <= 61; i ++ ) cf[i] = 2 * cf[i - 1];
}
void solve()
{
int a, b, r;
cin >> a >> b >> r;
if (a < b) swap(a, b);
int ans = a - b;
int res = ans;
vector<int> aa, bb;
if (a == 0) aa.push_back(0);
if (b == 0) bb.push_back(0);
int at = a, bt = b;
while (at)
{
aa.push_back(at & 1);
at >>= 1;
}
while (bt)
{
bb.push_back(bt & 1);
bt >>= 1;
}
while (bb.size() < aa.size()) bb.push_back(0);
int i = bb.size() - 1;
int tmp = r;
bool flag = false;
while (i >= 0)
{
if (!flag && aa[i] != bb[i])
{
flag = true;
if (aa[i] == 0)
{
if (cf[i] > r) break;
else
{
r -= cf[i];
ans -= 2 * cf[i];
res = min(res, abs(ans));
i -- ;
continue;
}
}
i -- ;
continue;
}
if (r < cf[i] || aa[i] == bb[i])
{
i -- ;
continue;
}
if (!flag && aa[i] == 0 && cf[i] >= r) break;
if (aa[i] == 1 && bb[i] == 0 && flag)
{
r -= cf[i];
ans -= cf[i] * 2;
res = min(abs(ans), res);
}
i -- ;
}
r = tmp;
i = bb.size() - 1;
ans = b - a;
flag = false;
while (i >= 0)
{
if (!flag && aa[i] != bb[i])
{
flag = true;
if (aa[i] == 1)
{
if (cf[i] > r) break;
else
{
r -= cf[i];
ans -= 2 * cf[i];
res = min(res, abs(ans));
i -- ;
continue;
}
}
i -- ;
continue;
}
if (r < cf[i] || aa[i] == bb[i])
{
i -- ;
continue;
}
if (!flag && aa[i] == 0 && cf[i] >= r) break;
if (bb[i] == 1 && aa[i] == 0 && flag)
{
r -= cf[i];
ans -= cf[i] * 2;
res = min(abs(ans), res);
}
i -- ;
}
cout << res << '\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
init();
int t = 1;
cin >> t;
while (t -- )
{
solve();
}
}
题目链接
这题只能想到二分了,后面的单调队列没用过(极大可能是用过没总结就忘记了)
二分最终答案,然后让区间和都满足条件去判断端点和是否满足条件
dp[i]
表示以i
为最后一个分割点,最小的端点和是多少
j
表示以i
为最后一个端点,它前面能取到的最大的端点序号值
用sum
记录当前区间和,每当区间和超过mid
了就把最前面一个值删掉,更新j
然后用单调队列维护以i
为最后一个端点能取到的端点中端点和最小的序号,所以转移方程为dp[i] = dp[q.front()] + a[i]
关于单调队列总结在这
#include
using namespace std;
#define int long long
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
const int N = 1000010;
void solve()
{
int n;
cin >> n;
vector<int> a(n + 2);
for (int i = 1; i <= n; i ++ ) cin >> a[i];
auto check = [&](int mid)
{
vector<int> dp(n + 2);
deque<int> q;
int sum = 0;
int j = 0;
q.push_front(0);
for (int i = 1; i <= n + 1; i ++ )
{
while (sum > mid) sum -= a[ ++ j];
while (q.size() && q.front() < j) q.pop_front();
if (!q.size()) return false;
dp[i] = dp[q.front()] + a[i];
while (q.size() && dp[q.back()] > dp[i]) q.pop_back();
q.push_back(i);
sum += a[i];
}
return dp[n + 1] <= mid;
};
int l = 0, r = 1e15;
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << r << '\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t -- )
{
solve();
}
}
题目链接
这题还是挺妙的
目标是给数组排序,还有个用于比较的元素 x,通过这个想到题目的交互可能是利用快排的思想,小于x的丢到左边,大于丢到右边,然后左右两边再递归排序
数据已经有序的时候快排会退化到 O ( n 2 ) O(n^2) O(n2),所以做之前先打乱顺序
#include
using namespace std;
#define int long long
void solve()
{
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
int n;
cin >> n;
vector<int> pos(n); // 存储值i的位置
for (int i = 0; i < n; i ++ ) pos[i] = i;
shuffle(pos.begin(), pos.end(), rnd);
auto ask = [&](int x)
{
cout << "? " << x + 1 << endl;
char nn;
cin >> nn;
return nn;
};
function<void(int, int)> qsort = [&](int l, int r)
{
if (l >= r) return;
int mid = pos[l + r >> 1];
while (ask(mid) != '=') ;
vector<int> a, b, c; // = > <
for (int i = l; i <= r; i ++ )
{
if (pos[i] == mid) a.push_back(pos[i]);
else
{
char t = ask(pos[i]);
ask(mid);
if (t == '<') c.push_back(pos[i]);
else b.push_back(pos[i]);
}
}
copy(c.begin(), c.end(), pos.begin() + l);
copy(b.begin(), b.end(), pos.begin() + c.size() + l + 1);
pos[l + c.size()] = mid;
qsort(l, l + c.size() - 1);
qsort(l + c.size() + 1, r);
};
qsort(0, n - 1);
vector<int> ans(n);
for (int i = 0; i < n; i ++ ) ans[pos[i]] = i;
cout << "!";
for (int i = 0; i < n; i ++ ) cout << ' ' << ans[i] + 1;
cout << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t -- )
{
solve();
}
}