思路:出了位运算其实需要按位进行考虑,不过一拿到这个题的时候变量较多,不好下手。
但是如果能定下一个量,就可以方便了。考虑二分z的上界,假如知道了z的上界,那么后面的式子就可以通过位运算的角度去枚举出最优解。
那么怎么去知道z的上界,由于是等差数列,所以整个式子可以变成一个单调递增的(sort)。而且贪心去考虑,优先把数字大的变成min(Ai,z)更优。于是sort从小到大序列,然后从后面开始,o(n)枚举长度,在一个长度内确定下来能否满足有z使得前面不变的和+后面变小的和<=y。从而获得z的上界。
已经有了z的范围[1,max],如果k第i位为1,那么我们z这一位尽量去放1,在不超过上界的情况下,如此能让该位异或变0,从而z^k+k-z变小。如果k第i位是0,如果放1和0有什么区别嘛。放1的话后面的z变大2^i,整体式子减小2^i,但是注意该位和k的异或会导致增大2^i.所以做无用功,还浪费到上界的有效大小。所以给予0即可。
最后累加枚举最小。
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=1e5+100;
typedef long long LL;
LL a[maxn];
LL n,A1,d,k,y;
int main(void)
{
cin.tie(0);std::ios::sync_with_stdio(false);
cin>>n>>A1>>d>>k>>y;
LL total=0;
for(LL i=1;i<=n;i++){
a[i]=A1+(i-1)*d;
total+=a[i];
}
sort(a+1,a+1+n);
LL ss=0;
LL ans=1e18;
for(LL i=n;i>=1;i--){
ss+=a[i];
LL l=-1;LL r=a[i]-1;
while(l<r)
{
LL mid=(l+r+1)>>1;
if(total-ss+mid*(n-i+1)<=y) l=mid;
else r=mid-1;
}
if(l==-1) continue;///在此区间内就算k取0都没法满足,不合法区间
LL high=l;
LL res=0;
for(LL j=60;j>=0;j--)
{
if((k>>j)&1){
if(res+(1<<j)<=high){
res+=(1<<j);
}
}
}
ans=min(ans,(n-i+1)*( (res^k)+k-res) );
}
cout<<ans<<endl;
return 0;
}