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

 

你可能感兴趣的:(codeforces)