Luogu3220 HNOI2012 与非 数位DP

传送门

题意:给出$N$个范围在$[0,2^k-1]$的整数,定义位运算$NAND$为位运算$AND$的逆运算,求$[L,R]$中有多少数能成为若干个前面给出的整数、若干括号和$NAND$运算组成的表达式的结果(每一个数在一个表达式中可以出现多次)。


OI生涯第一道数位DP

可以使用$NAND$表示所有基本位运算(这个可以手玩出来qwq),那么$NAND$像基本位运算一样会有一个性质:如果所有给出的整数中第$i$位和第$j$位相同,那么最后的结果的第$i$位与第$j$位也一定相同,而不满足这个条件的位在一个确定了之后,另一位仍然可以同时取$0$或$1$(基于线性基的思想可以证明这个结论),那么我们可以预处理出所有互相影响的位,然后数位$DP$即可。

数位$DP$留在以后的专题??反正现在不想写,实在不懂看下面的code吧

 

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 //This code is written by Itst
 4 using namespace std;
 5 
 6 inline ll read(){
 7     ll a = 0;
 8     bool f = 0;
 9     char c = getchar();
10     while(c != EOF && !isdigit(c)){
11         if(c == '-')
12             f = 1;
13         c = getchar();
14     }
15     while(c != EOF && isdigit(c)){
16         a = (a << 3) + (a << 1) + (c ^ '0');
17         c = getchar();
18     }
19     return f ? -a : a;
20 }
21 
22 const int MAXN = 1010;
23 ll num[MAXN] , K , N , p[61];
24 bool vis[MAXN];
25 vector < int > influ[61];
26 
27 inline ll poww(ll a , int b){
28     ll times = 1;
29     while(b){
30         if(b & 1)
31             times = times * a;
32         a = a * a;
33         b >>= 1;
34     }
35     return times;
36 }
37 
38 ll dfs(int now , ll sum , ll limit){
39     if(now == -1 || !p[now])
40         return 1;
41     if(vis[now])
42         return dfs(now - 1 , sum , limit);
43     ll s = sum;
44     for(int i = 0 ; i < influ[now].size() ; i++)
45         s |= 1ll << influ[now][i];
46     if(s <= limit)
47         return dfs(now - 1 , s , limit) + poww(2 , p[now] - 1);
48     else
49         return dfs(now - 1 , sum , limit);
50 }
51 
52 int main(){
53 #ifdef LG
54     freopen("3220.in" , "r" , stdin);
55     //freopen("3220.out" , "w" , stdout);
56 #endif
57     N = read();
58     K = read();
59     ll L = read() , R = read();
60     for(int i = 1 ; i <= N ; i++)
61         num[i] = read();
62     for(int i = K - 1 ; i >= 0 ; i--){
63         if(vis[i])
64             continue;
65         influ[i].push_back(i);
66         for(int j = i - 1 ; j >= 0 ; j--){
67             if(vis[j])
68                 continue;
69             bool f = 1;
70             for(int k = 1 ; f && k <= N ; k++)
71                 f = ((num[k] >> i) & 1) == ((num[k] >> j) & 1);
72             if(f){
73                 vis[j] = 1;
74                 influ[i].push_back(j);
75             }
76         }
77     }
78     for(int i = 0 ; i < K ; i++)
79         p[i] = (i ? p[i - 1] : 0) + !vis[i];
80     printf("%lld" , dfs(K - 1 , 0 , R) - (L ? dfs(K - 1 , 0 , L - 1) : 0));
81     return 0;
82 }

 

转载于:https://www.cnblogs.com/Itst/p/9911371.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值