题意:给出$N$个范围在$[0,2^k-1]$的整数,定义位运算$NAND$为位运算$AND$的逆运算,求$[L,R]$中有多少数能成为若干个前面给出的整数、若干括号和$NAND$运算组成的表达式的结果(每一个数在一个表达式中可以出现多次)。
OI生涯第一道数位DP
可以使用$NAND$表示所有基本位运算(这个可以手玩出来qwq),那么$NAND$像基本位运算一样会有一个性质:如果所有给出的整数中第$i$位和第$j$位相同,那么最后的结果的第$i$位与第$j$位也一定相同,而不满足这个条件的位在一个确定了之后,另一位仍然可以同时取$0$或$1$(基于线性基的思想可以证明这个结论),那么我们可以预处理出所有互相影响的位,然后数位$DP$即可。
数位$DP$留在以后的专题??反正现在不想写,实在不懂看下面的code吧
#include<bits/stdc++.h> #define ll long long //This code is written by Itst using namespace std; inline ll read(){ ll a = ; ; char c = getchar(); while(c != EOF && !isdigit(c)){ if(c == '-') f = ; c = getchar(); } while(c != EOF && isdigit(c)){ a = (a << ) + (a << ) + (c ^ '); c = getchar(); } return f ? -a : a; } ; ll num[MAXN] , K , N , p[]; bool vis[MAXN]; vector < ]; inline ll poww(ll a , int b){ ll times = ; while(b){ ) times = times * a; a = a * a; b >>= ; } return times; } ll dfs(int now , ll sum , ll limit){ || !p[now]) ; if(vis[now]) , sum , limit); ll s = sum; ; i < influ[now].size() ; i++) s |= 1ll << influ[now][i]; if(s <= limit) , s , limit) + poww( , p[now] - ); else , sum , limit); } int main(){ #ifdef LG freopen("3220.in" , "r" , stdin); //freopen("3220.out" , "w" , stdout); #endif N = read(); K = read(); ll L = read() , R = read(); ; i <= N ; i++) num[i] = read(); ; i >= ; i--){ if(vis[i]) continue; influ[i].push_back(i); ; j >= ; j--){ if(vis[j]) continue; ; ; f && k <= N ; k++) f = ((num[k] >> i) & ) == ((num[k] >> j) & ); if(f){ vis[j] = ; influ[i].push_back(j); } } } ; i < K ; i++) p[i] = (i ? p[i - ] : ) + !vis[i]; printf( , , R) - (L ? dfs(K - , , L - ) : )); ; }