Fork me on GitHub

Codeforces 460D Little Victor and Set --分类讨论+构造

题意:从区间[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;
}
View Code

 

posted @ 2015-01-16 20:25  whatbeg  阅读(254)  评论(0编辑  收藏  举报