首先这个题可以记忆化搜索,不过直接记忆化复杂度是不能满分的,需要加可行性剪枝:就是当前的状态肯定无解时,就要直接返回。
#include<bits/stdc++.h>
using namespace std;
const int maxn=55,lim=(1<<21)-1;
inline int read(){
char c=getchar();int t=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
return t*f;
}
int n,a[maxn],cho[maxn],ans,suf[maxn];
struct pii{
int a,b;
pii(int aa=0,int bb=0){a=aa,b=bb;}
};
bool operator <(pii a,pii b){
if(a.a==b.a)
return a.b<b.b;
return a.a<b.a;
}
map<pii,long long> mp[55];
int alfa1,alfa2;
long long dfs(int now,int tmp1,int tmp2){
alfa1=(tmp1&suf[now])^tmp2,alfa2=(tmp2&suf[now])^tmp1;
if((alfa1&tmp2)!=alfa1){return 0;}
if((alfa2&tmp1)!=alfa2){return 0;}
pii tmp=pii(tmp1,tmp2);
if(mp[now][tmp]){
return mp[now][tmp];
}
if(now==n+1){
if(tmp1==tmp2){
mp[now][tmp]=1;return 1;
}
return 0;
}
long long ans=0;
ans+=dfs(now+1,tmp1&a[now],tmp2);
ans+=dfs(now+1,tmp1,tmp2&a[now]);
mp[now][tmp]=ans;
return ans;
}
int main(){
//freopen("b.in","r",stdin);
//freopen("b.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)a[i]=read();
suf[n+1]=lim;
for(int i=n;i>=1;i--)suf[i]=suf[i+1]&a[i];
printf("%lld\n",dfs(1,lim,lim));
return 0;
}
然后正解是按位容斥,具体就是观察这些数的二进制,然后考虑不合法的情况是什么:对于某一位二进制,如果所有在这一位是1的数都被选进了一个集合,那这个情况就不合法,直接算不好算,所以需要
O
(
2
20
)
枚
举
钦
定
哪
些
位
不
合
法
的
状
态
O(2^{20})枚举钦定哪些位不合法的状态
O(220)枚举钦定哪些位不合法的状态容斥,系数就是
−
1
不
合
法
的
位
数
-1^{不合法的位数}
−1不合法的位数,然后此时有一个问题,就是不能有一个集合是全空的,所以一开始所有不满足条件的方案数不是
2
n
2^n
2n.
容斥过程中的方案数也没有那么简单。具体看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
char c=getchar();int t=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
return t*f;
}
int n,a[55];
bitset<55> alfa[25],beta[25],A,B,C,W;
int len,pos[25],ans;
void dfs(int now,int w,bitset<55> a,bitset<55> b,bitset<55> c){//a维护的是某些位置的值具有相关性(即哪些位置是必须放在一个集合的)
W=b&c;
if(W.count())return ;//某个关键集合既出现在a又出现在b,枚举的情况不合法
if(now>len){
if(c.count()&&b.count())ans=ans+w*(1ll<<a.count());
else ans=ans+w*((1ll<<a.count())-1);//两个集合中的一个没有限制,会出现某个集合是空集的情况,要去掉
return ;
}
dfs(now+1,w*(-1),a&alfa[pos[now]],b|beta[pos[now]],c);//将所有0加入b集合
dfs(now+1,w*(-1),a&alfa[pos[now]],b,c|beta[pos[now]]);//将所有0加入c集合
dfs(now+1,w,a,b,c);
}
signed main(){
//freopen("b.in","r",stdin);
//freopen("b.out","w",stdout);
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
for(int j=0;j<20;j++){
alfa[j][i]=(a[i]&(1<<j));
beta[j][i]=1-alfa[j][i];
}
}
len=0;
for(int i=0;i<20;i++){
if((alfa[i].count()!=n)&&(alfa[i].count())){pos[++len]=i;}
}
for(int i=1;i<=n;i++)A[i]=1;
dfs(1,1,A,B,C);
printf("%lld\n",ans-1);//两个集合都没有限制的情况在上面的dfs中没有考虑
return 0;
}