题解 或与异或
题目描述
具体做法与心路历程
没什么好的想法,只能想到一个 n S 2 nS^2 nS2的 d p dp dp。 ( S = 2 14 ) (S=2^{14}) (S=214)。
设 f [ i ] [ X o r ] [ O r ] f[i][Xor][Or] f[i][Xor][Or]表示前 i i i个数中异或值为 X o r Xor Xor,或值为 O r Or Or的方案数。转移显然。
发现跑个 a i ≤ 50 , n = 50 a_i \leq 50,n=50 ai≤50,n=50的样例要跑 6 s 6s 6s。于是用 v i s i vis_i visi表示当前 f [ i ] f[i] f[i]有没有值,瞬间只跑了
60 m s 60ms 60ms。 W o W WoW WoW,这令人惊讶的优化速度!于是让我想打个前向星来优化了。。
但是空间开不下!!!于是喜提 60 60 60分。
具体做法
这道题可以用状压也可以枚举子集。
对于我刚刚设的方程,我们把它改一下。
我们考虑枚举 O r Or Or的值,然后把所有 a i & O r = a i a_i\&Or=a_i ai&Or=ai的所有值提出来。
那么设 f i , X o r f_{i,Xor} fi,Xor表示提取出来的前 i i i个中异或值为 X o r Xor Xor的方案数。
那么有 f i , X o r = f i − 1 , X o r + f i − 1 , X o r ⨁ a i f_{i,Xor}=f_{i-1,Xor}+f_{i-1,Xor\bigoplus a_i} fi,Xor=fi−1,Xor+fi−1,Xor⨁ai
时间复杂度为 O ( n S 2 ) O(nS^2) O(nS2)。但这里有一个结论是 1 1 1~ 2 n 2^n 2n的所有数的子集个数为 O ( 3 n ) O(3^n) O(3n)。
所以我们在枚举 O r Or Or后 d p dp dp只转移 O r Or Or的子集。那么时间复杂度即为 O ( n 3 14 ) O(n3^{14}) O(n314)了。
/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年10月28日 星期一 14时29分16秒
*******************************/
#include<cstdio>
#include<algorithm>
using namespace std;
struct IO{
template<typename T>
IO & operator>>(T&res)
{
T q=1;char ch;
while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
res=(ch^48);
while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
res*=q;
return *this;
}
}cin;
const int maxn=55;
const int S=(1<<13);
long long n,a[maxn],f[55][S+1],tmp[S+1],ans,cnt;
int main()
{
//freopen("orandxor.in","r",stdin);
//freopen("orandxor.out","w",stdout);
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=0;i<=S;i++)
{
cnt=0;
for(int j=1;j<=n;j++)
if((a[j]|i)==i)
tmp[++cnt]=a[j];
f[0][0]=1;
for(int j=1;j<=cnt;j++)
{
f[j][0]=f[j-1][tmp[j]]+f[j-1][0];
for(int k=i;k;k=(k-1)&i)
f[j][k]=f[j-1][k^tmp[j]]+f[j-1][k];
}
ans+=f[cnt][i];
}
printf("%lld\n",ans-1);
return 0;
}