题解:P16995 【MX-S15-T1】「DLESS-5」Another OR Problem
P16995 【MX-S15-T1】「DLESS-5」Another OR Problem
好题!
看到这个按位或求
设
考虑
设
考虑分讨:
- 当二进制下的每一个
c_i 的末尾都为1 时,可以选择末尾连续1 的数量最小的c_i 加一,这样,f_{k+1}(a) \ge f_{k}(a) 。 - 当存在二进制下的
c_i 末尾为0 ,可以直接对这个c_i 加一,这时,f_{k+1}(a) \ge f_{k}(a) 。
综上,
然后就按位枚举,考虑怎么写
如果已经存在
否则如果让
这样就能获得
考虑正解。
当进行完操作后
#include<bits/stdc++.h>
const int MAXN = 1e6 + 5;
#define pb push_back
typedef long long LL;
typedef std::pair<int, int> PII;
typedef std::pair<LL, int> PLI;
int n; LL k, a[MAXN], b[MAXN];
LL sum;
inline bool check(int x)
{
for(int i = 1; i <= n; i++)
if((a[i] >> x) & 1)
return 1;
PLI ans = {LLONG_MIN, 0};
for(int i = 1; i <= n; i++)
ans = std::max(ans, {a[i] % (1ll << x), i});
LL nd = (1ll << x) - ans.first;
// std::cout << sum << ' ' << nd << ' ' << ans.first << ' ' << (1ll << x) << '\n';
if(sum + nd <= k)
{
sum += nd, a[ans.second] += nd, b[ans.second] += nd;
// std::cout << x << '\n';
return 1;
}
else return 0;
}
inline void solve()
{
scanf("%d%lld", &n, &k);
for(int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
for(int i = 1; i <= n; i++)
b[i] = 0;
LL ans = 0; sum = 0;
for(int j = 60; ~j; j--)
if(check(j))
ans |= (1ll << j);
k -= sum;
if(k) for(int j = 60; ~j; j--)
{
if(!((k >> j) & 1)) continue;
bool fl = 0;
for(int i = 1; i <= n && !fl; i++)
if(!((a[i] >> j) & 1))
a[i] += (1ll << j), b[i] += (1ll << j), fl = 1;
if(fl) continue;
PII ans = {INT_MAX, 0};
for(int i = 1; i <= n; i++)
{
LL x = (a[i] >> j);
int cnt = 0;
while(x)
{
if(!(x & 1))
break;
x >>= 1, cnt++;
}
ans = std::min(ans, {cnt, i});
}
a[ans.second] += (1ll << j), b[ans.second] += (1ll << j);
}
printf("%lld\n", ans);
for(int i = 1; i <= n; i++)
printf("%lld%c", b[i], (i == n ? '\n' : ' '));
}
int main()
{
int c, T;
scanf("%d%d", &c, &T);
while(T--)
solve();
return 0;
}
::::