题意:从区间[L,R]中选取不多于k个数,使这些数异或和尽量小,输出最小异或和以及选取的那些数。
解法:分类讨论。
设选取k个数。
1. k=4的时候如果区间长度>=4且L是偶数,那么可以构造四个数(L,L+1,L+2,L+3),这样的话(L^(L+1)) ^ ((L+2)^(L+3)) = 0,最优
如果L不是偶数,那么看从L+1到R有没有四个数,如果有则取该四个数,否则最小异或和达不到0,也达不到1了,不再考虑k=4,k=3时还有可能等于0,所以转到k=3
2. k=3时,要使异或和为0,那么要选取a,b,c,设a<b<c,三个数至少两位表示,且不能为0,所以我们这么构造:
11000...
10111...
01111...
因为 c 应尽量小,所以后面补0,那么下面两个后面全部补1.
我们枚举一个一个加位数,看是否有R>=c>a>=L,如果有,那么最小异或和为0,如果没有,那么异或和就最小是1了,如果有大于等于三个数的话,我们取两个数就能达到1(一定可以以偶数开头),所以此时取三个一定不比取两个更优,所以k=2就能解决,转k=2
3. k=2时,看有没有偶数开头的两个数(2x,2x+1),这时异或和为1,已经是当下最小的了,否则就只能比较L和L^R的大小再输出了。
4. k=1, 显然只能取一个数的时候,选的越小越好,选L
可以证明,k > 4的情况,一定可以通过取k<=4个达到异或和最小。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> #define lll __int64 using namespace std; #define N 100107 int main() { lll l,r; int k; while(scanf("%I64d%I64d%d",&l,&r,&k)!=EOF) { if(k >= 4) { //0 if(l%2 && r-l+1 >= 5) { printf("0\n4\n%I64d %I64d %I64d %I64d\n",l+1,l+2,l+3,l+4); continue; } else if(l%2 == 0 && r-l+1 >= 4) { printf("0\n4\n%I64d %I64d %I64d %I64d\n",l,l+1,l+2,l+3); continue; } k = 3; } if(k == 3) { //0 lll c = 3LL, b = 2LL, a = 1LL; int flag = 0; while(1) { if(c <= r && a >= l) { flag = 1; break; } if(c > r) break; a = a<<1|1; b = b<<1|1; c = c<<1; } if(flag) { printf("0\n3\n%I64d %I64d %I64d\n",a,b,c); continue; } k = 2; } if(k == 2) { //1 int flag = 0; for(lll i=l;i<=l+1;i++) { if(i%2LL == 0 && i+1LL <= r) { flag = 1; printf("1\n2\n%I64d %I64d\n",i,i+1LL); } } if(!flag) { if((l^r) < l) printf("%I64d\n2\n%I64d %I64d\n",l^r,l,r); else printf("%I64d\n1\n%I64d\n",l,l); } } if(k == 1) { //unkown printf("%I64d\n1\n%I64d\n",l,l); continue; } } return 0; }
时间: 2024-12-29 12:30:02