「コードフォース」D. 無限セット

D. 無限集合

https://codeforces.com/contest/1635/problem/D

トピックの説明

さまざまな正の整数と無限のセット S で構成される配列があるので、セット S をすべてxxで埋める必要があります。x条件の数。

xxxの条件(いずれか 1 つを満たすことができます):

  1. x = ai ( 1 ≤ i ≤ n ) x = a_i(1\leq i\leq n)バツ=a( 1n )
  2. x = 2 y + 1 ( y ∈ S ) x = 2y + 1 (y \in S)バツ=2+1 ( yεS )yyy はS セット内になければなりません)
  3. x = 4 y ( y ∈ S ) x = 4y (y \in S)バツ=4(εS ) ($y$ は S セットにある必要があります)

無限集合 S で2 p 2^p未満のものをすべて見つける2pの数。

説明を入力

最初の行には、2 つの整数 n と p (1≤n,p≤2⋅10 5 ) が含まれます。

第二行包含 n 个整数 a1,a2,…,an (1≤ai≤109)。

保证 a 中的所有数字都是不同的。

输出描述

打印一个整数,即 S 中严格小于 2p 的元素数。 请记住以 109+7 为模打印。

样例

#1

2 4
6 1
9

#2

4 7
20 39 5 200
14

#3

2 200000
48763 1000000000
448201910

提示

在第一个示例中,小于 24 的元素是 {1,3,4,6,7,9,12,13,15}。

在第二个例子中,小于 27 的元素是 {5,11,20,23,39,41,44,47,79,80,83,89,92,95}。

解析

有一说一,二进制的题目我确实没写过…,一时间也只能想到暴力,可这样不可能过,后面看大佬题解,发现要用二进制…

**Tips:**以后但凡看到这种 2 p 2^p 2p 次幂的形式,要往二进制想想。

根据二进制, x x x 的第二和第三条件可以转为:

  1. 第一条件其实就是 A 数组的所有元素均属于 S
  2. 2 x + 1 2x+1 2x+1 相当于 2 < < 1 2 << 1 2<<1 然后加 1 1 1 (向后添加一个 1)
  3. 4 x 4x 4x 相当于 4 < < 2 4 << 2 4<<2 (向后添加两个 0)

题目要我们求 S 中所有小于 2 p 2^p 2p 的数,其实就是求对应的二进制最高位 1 的后面 0 的变化,例如 p = 3 p=3 p=3 时,对应二进制为 1000 1000 1000 ,我们可以得到且不保证符合的有 0000 、 0100 、 0110 、 0111 、 0010 、 0011 、 0001 0000、0100、0110、0111、0010、0011、0001 0000010001100111001000110001

通过上面的例子,我们发现,只是 1 后面的 0 发生了变化,而我们的规则是要么增加一个 1,要么增加两个0(这0是一起添加的,不能分开)。

我们假设一个数为 x x x ,那么我们可以得到:

x x x 增加位数 得到的不同的数
0 x x x 1
1 x 1 x1 x1 1
2 x 00 、 x 11 x00、x11 x00x11 2
3 x001、x100、x111 x001、x100、x111× 001 × 100 × 111 3
4 x 0000 、 x 1100 、 x 0011 、 x 1111 、 x 1001 x0000、x1100、x0011、x1111、x1001× 0000 × 1100 × 0011 × 1111 × 1001 5

上の表の意味: 数値の 2 進数の後に桁数が続き、毎回 1 か 2 つの 0 しか追加できないため、最終的にいくつの異なる数値が得られるかを示します。

そして、それらがどのように変化しても、一意であることが保証されていることがわかります。

これはフィボナッチ数列であることがわかりますF [ i ] = F [ i − 1 ] + F [ i − 2 ] F[i] = F[i-1] + F[i-2]F [ i ]=F [ i1 ]+F [ i2 ]、ここでは数値が増加することを意味しますiiiビットで取得できる異なる数の数はF [ i ] F[i]F []

今は桁数を追加するための数値を求めているだけです.異なる数値を取得した場合は、前の数値をカウントしないため、接頭辞と接頭辞 [ i ] = 接頭辞 [ i − 1 ]を作成する必要があります。+ F [ i ] 接頭辞[i] = 接頭辞[i-1] + F[i]p re f i x [ i ]=p re f i x [ i1 ]+F [ i ]の場合、定義はprefix [ i ] prefix[i]p re f i x [ i ]は増加を意味しますiii桁の後に得られるすべての異なる数字

最後に、数値ai a_iの場合、aAj a_j は別の数値から取得できますa、二重にカウントしないように、ai a_iのみを保持しますaたとえば、A には 2 と 8 の 2 つの要素があり、8 は 2 を介して取得できるため、2 だけを保持する必要があります。

2:0010

8:1000

0010 は 1 回変更できます (つまり、最後に 2 つの 0 を追加します) ==> 0010 00 ==> 10 00

ACコード

public class Main {
    
    
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st = new StreamTokenizer(br);
    static PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out));

    public static void main(String[] args) throws Exception {
    
    
        final int MOD = 1000000000 + 7;
        final int MAX = 200005;
        int n = nextInt(), p = nextInt(), ans = 0;
        int[] A = new int[MAX];
        int[] F = new int[MAX];
        int[] prefix = new int[MAX];
        
        F[0] = F[1] = 1;
        prefix[0] = 1; prefix[1] = 2;
        HashMap<Integer, Boolean> map = new HashMap<>();

        for(int i = 2; i < MAX; i++) {
    
    
            F[i] = (F[i-1] + F[i-2]) % MOD;
            prefix[i] = (prefix[i-1] + F[i]) % MOD;
        }

        for(int i = 1; i <= n; i++) {
    
    
            A[i] = nextInt();
            map.put(A[i], true);
        }
	    
        // 对数组元素进行去重
        for(int i = 1; i <= n; i++) {
    
    
            int x = A[i];
            while(x != 0) {
    
    
                if(x % 2 == 1) x >>= 1; // 条件2,末尾加 1
                else if(x % 4 == 0) x >>= 2; // 条件3,末尾加两个 0
                else break;
                if(map.containsKey(x)) {
    
     // 判断 x 是否已经存在 S 中
                    map.remove(A[i]); // 若存在,则表示 A[i] 可以通过其它元素得到,估抛弃该元素
                    break;
                }
            }
        }
        
        Set<Integer> set = map.keySet();
        for(Integer x : set) {
    
    
            int bit = 32 - Integer.numberOfLeadingZeros(x); // 获取 x 二进制位数的前导零个数
            if(bit > p) continue;
            ans += prefix[p - bit];
            ans %= MOD;
        }
        
        System.out.println(ans);
    }
    
    public static int nextInt() throws Exception {
    
    
        st.nextToken();
        return (int) st.nval;
    }
}

おすすめ

転載: blog.csdn.net/qq_43098197/article/details/130483906