CF1635D

此做法用到 $\text{Trie}$ 和 $\text{DP}$。

首先,先对操作进行一个简单的转换:

  1. $2x+1$ 等价于在 $x$ 的二进制串后面加上一个 $1$。

    例如:$x=5,2x+1=11$,等价于在 $5$ 的二进制 $101$ 后面加上一个 $1$ 得到 $1011$,即 $11$ 的二进制。

  2. $4x$ 等价于在 $x$ 的二进制串后面加上 $2$ 个 $0$。

    例如:$x=5,4x=20$,等价于在 $5$ 的二进制 $101$ 后面加上 $00$ 得到 $10100$,即 $20$ 的二进制。

这时能发现只要保证所有的 $a_i$ 两两不能被对方进行上述操作所到达,那么就能用 $\text{DP}$ 方程式 $f_i=f_{i-1}+f_{i-2}$ 去求解最终答案 $f_p$。

判断 $x$ 能否通过操作得到 $y$ 的必要条件:

  1. $x<y$。
  2. $x$ 的二进制串是 $y$ 的二进制串的真前缀。

当然,这只是必要条件,并不是充分条件。比如二进制串 $101$ 不能够到达 $10101$,但这一对数却满足上述条件。

所以我们还要满足 $y$ 的子串 $y[\text{size}(x)+1:\text{size}(y)]$ 中,所有的 $0$ 都是成对地出现的。

具体的,我们用 $\text{DP}$ 预处理 $y[\text{size}(x)+1:\text{size}(y)]$ 中,所有的 $0$ 是否都是成对出现的;用字典树来完成判断是否存在真前缀。

时间复杂度 $O(n\log V)$。

#include <bits/stdc++.h>
#define L(i, a, b) for(int i = (a); i <= (b); i++)
#define R(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
const int N = 2e5 + 10, mod = 1e9 + 7;
int n, p, ans, a[N], f[N], b[N], f1[N][30];
int get(int x, int i){return (x >> i) & 1;}
struct Trie{
    int tot, t[N * 30][2], end[N * 30];
    void insert(int x, int id){
        int p = 0;
        R(i, b[id], 0){
            int e = get(x, i);
            if(!t[p][e]) t[p][e] = ++tot;
            p = t[p][e];
        }
        end[p] = 1;
    }
    int query(int x, int id){
        int p = 0;
        R(i, b[id], 0){
            int e = get(x, i);
            if(!t[p][e]) return 0;
            p = t[p][e]; if(end[p] && f1[id][i - 1]) return 1;//
        }
        return 0;
    }
} t;
int main(){
    scanf("%d%d", &n, &p);
    L(i, 1, n) scanf("%d", &a[i]);
    sort(a + 1, a + n + 1);
    L(i, 1, n){
        R(j, 29, 0) if(get(a[i], j)){b[i] = j; break;}
        f1[i][0] = get(a[i], 0), f1[i][1] = (get(a[i], 1) == get(a[i], 0));
        L(j, 2, b[i]){
            if(get(a[i], j)) f1[i][j] = f1[i][j - 1]; 
            else f1[i][j] = get(a[i], j - 1)? 0 : f1[i][j - 2];
        }
    }
    L(i, 1, n) if(!t.query(a[i], i))
        f[b[i] + 1]++, t.insert(a[i], i);
    ans = f[1];
    L(i, 2, p){
        (f[i] += (f[i - 1] + f[i - 2]) % mod) %= mod;
        (ans += f[i]) %= mod;
    }
    printf("%d\n", ans);
    return 0;
}
posted @ 2023-07-06 11:11  徐子洋  阅读(11)  评论(0)    收藏  举报  来源