题解 或与异或

题解 或与异或

题目描述

_216b05.png

_39c10f.png

具体做法与心路历程

没什么好的想法,只能想到一个 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 ai50,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=fi1,Xor+fi1,Xorai

时间复杂度为 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值