题目描述
传送门
题意
给定一个区间l,r与一个数k.
在l~r中找不超过k个数(不能重复选),使得这些数的异或和最小,输出方案.
1<=l<=r<=10^12,k<=10^6
题解
分几种情况讨论。
1:r-l+1<=4。
2:k=1。
3:k=2。
4:k>=4。
5:k=3。
第一种情况:2^4暴力。
第二种情况:l即为解。
第三种情况:一定能构造出异或和为1的解。
第四种情况:一定能构造出异或和为0的解。
第五种情况:
首先一定能构造出异或和为1的解。
令m表示2^m大于l且最小的值。
设x=2^m-1,y=2^m+2^(m-1),z=2^m+2^(m-1)-1就是一个异或和为0的解,如果此时y>r,则答案至少是1。
代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long
const LL INF=1000000000000000000LL;
LL l,r,k,mul,m,x,y,z;
LL mi[100];
LL ans,a[10],ANS[10];
int vis[10],sum;
inline void dfs(LL dep)
{
if (dep==r+1)
{
LL now=0; int cnt=0;
for (int i=1;i<=r-l+1;++i)
if (vis[i])
{
a[++cnt]=i+l-1;
now^=(i+l-1);
}
if (cnt>k||!cnt) return;
if (now<ans)
{
ans=now;
sum=cnt;
for (int i=1;i<=sum;++i)
ANS[i]=a[i];
}
return;
}
for (int i=0;i<=1;++i)
{
vis[dep-l+1]=i;
dfs(dep+1);
}
}
int main()
{
scanf("%I64d%I64d%I64d",&l,&r,&k);
if (r-l+1<5)
{
ans=INF;
dfs(l);
printf("%I64d\n%d\n",ans,sum);
for (int i=1;i<=sum;++i) printf("%I64d%c",ANS[i]," \n"[i==sum]);
return 0;
}
else
{
if (k==1)
{
printf("%I64d\n1\n%I64d\n",l,l);
return 0;
}
if (k==2)
{
if (l&1) l++;
puts("1");
puts("2");
printf("%I64d %I64d\n",l,l+1);
}
if (k>=4)
{
if (l&1) l++;
puts("0");
puts("4");
printf("%I64d %I64d %I64d %I64d\n",l,l+1,l+2,l+3);
return 0;
}
if (k==3)
{
mul=1; m=0;
for(;;mul*=2,m++)
{
mi[m]=mul;
if (mul>l) break;
}
x=mi[m]-1;
y=mi[m]+mi[m-1]-1;
z=mi[m]+mi[m-1];
if (z<=r)
{
puts("0");
puts("3");
printf("%I64d %I64d %I64d\n",x,y,z);
}
else
{
if (l&1) l++;
puts("1");
puts("2");
printf("%I64d %I64d\n",l,l+1);
}
return 0;
}
}
}
总结
一定要好好想!
异或和这个东西好好想!