数位DP:: SoSDP
学习博客(待补)
下面做一些例题:
SPECIAL PAIRS
题意
给n个数字,求这些数字有多少对的(AND) 结果是0。数字不大于1e6。顺序反相反视为不同的对。
思路
做一个桶排计数。对于每个数(a_i) ,与他(AND) 是0的数,就是反$ a_i$ 的子集。也即是SoSDP里的F所统计的量了。
代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e6+5;;
int a[MAXN];
int x[MAXN];
long long dp[MAXN];
int kase,n;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>kase;
while(kase--){
cin>>n;
memset(a,0,sizeof a);
memset(dp,0,sizeof dp);
for(int i=1;i<=n;i++){
cin>>x[i];
dp[x[i]]=++a[x[i]];
}
for(int i=0;i<=20;i++)
for(int mask=0;mask<=(1<<20);mask++){
if(mask&(1<<i)){
dp[mask]+=dp[mask^(1<<i)];
}
}
int mask=(1<<20)-1;
long long ans=0;
for(int i=1;i<=n;i++){
ans+=dp[(~x[i])&mask];
}
cout<<ans<<endl;
}
return 0;
}
/*
4
5
41 47 34 40 29
4
999956 999959 999993 999993
3
4 1 3
3
1 0 0e
*/
注意几个细节:
位数一定取到最大值的长度(假设是len),而答案要记录到((1)_B^{len}) 。我一开始答案只记录到最大值,导致一部分漏解了。
简化代码的写法就是每次都走到数据范围最大的解答,为了更加优化可以去找一下最大值作为边界。