数学(Anton and School,cf 734F)

34 篇文章 1 订阅
15 篇文章 0 订阅

首先要知道          (a&b)+(a|b)=a+b          ①

我也不知道怎么才能想到这个,只能说按位运算是有很多规律的,以后遇到类似的题可以尝试着去寻找一些规律。


讲解:

已知         b[i]=∑(a[i]&a[j]),(1<=j<=n)

                c[i]=∑(a[i]|a[j]),(1<=j<=n)


设d[i]=b[i]+c[i],

根据公式①,易得          d[i]=b[i]+c[i]=∑(a[i]&a[j])+∑(a[i]|a[j])=∑(a[i]&a[j]+a[i]|a[j])

                                                    =∑(a[i]+a[j])=n*a[i]+a[1]+a[2]+...+a[n],(1<=j<=n)

即                                  d[i]=n*a[i]+a[1]+a[2]+...+a[n]          ②

所以         ∑d[i]= ∑(b[i]+c[i])=∑(n*a[i]+a[1]+a[2]+...+a[n])=2n*(a[1]+a[2]+...+a[n])

即             a[1]+a[2]+...+a[n]=∑d[i]/(2n)          ③


讲③带入②,解得          a[i]=(d[i]-(∑d[i])/(2n))/n。

然后循环一遍就能求出唯一的a[i]。

但是要注意这个a[i]只是满足d[i],却不一定满足b[i]与c[i]。

所以要一定检查一遍,如果这个解同时也满足b[i]与c[i],那么就输出之,否则就无解。


至于检查的方式也是很有趣,如果带入原公式O(n^2)计算那就超时了。

方法是按位计算。因为计算公式很有特点,所以一个O(n)的循环就可以用一个O(1)的计算搞定,但付出的代价是必须一位一位的算。最终算法时间复杂度位O(nL),其中L是二进制位数。显然long long也就才64位啦。


a[i][j]表示第i个a的第j位,显然只能为0或者为1。b[i][j],c[i][j]同理。

bit[i]表示第i位是1的a的个数。

那么有b[i][k]=∑(a[i][k]&a[j][k]),(1<=j<=n,0<=k<L)

以及   c[i][k]=∑(a[i][k]|a[j][k]),(1<=j<=n,0<=k<L)

那么我们很容易就找到了这个O(1)计算的公式。

b[i][k]=a[i][k]?bit[k]:0

c[i][k]=a[i][k]?n:bit[k]


最后B[i]=∑(b[i][k]<<k)

以及C[i]=∑(c[i][k]<<k)


然后把B[i],C[i],与b[i],c[i]对比一下就知道答案是否符合了。


代码

#include<bits/stdc++.h>
#define maxn 200100
using namespace std;
typedef long long ll;

ll n;
ll a[maxn],b[maxn],c[maxn],d[maxn];
ll A[maxn][70],bit[70],B[maxn],C[maxn];
bool ok()
{
    for(ll i=1;i<=n;i++)
        if(a[i]<0)
            return false;
    for(ll i=1;i<=n;i++)
        for(ll j=0;j<63;j++)
        {
            A[i][j]=a[i]&(1ll<<j)?1:0;
            bit[j]+=A[i][j];
        }
    for(ll i=1;i<=n;i++)
        for(ll j=0;j<63;j++)
        {
            ll bbit=A[i][j]?bit[j]:0;
            ll cbit=A[i][j]?n:bit[j];
            B[i]+=bbit<<j;
            C[i]+=cbit<<j;
        }

    for(ll i=1;i<=n;i++)
        if(B[i]!=b[i]||C[i]!=C[i])
            return false;
    return true;
}

void print()
{
    for(ll i=1;i<=n;i++)
        printf("%I64d%c",a[i],i==n?'\n':' ');
}

int main()
{
    scanf("%I64d",&n);
    for(ll i=1;i<=n;i++)
        scanf("%I64d",&b[i]);
    for(ll i=1;i<=n;i++)
    {
        scanf("%I64d",&c[i]);
        d[i]=b[i]+c[i];
    }
    ll sum=0;
    for(ll i=1;i<=n;i++)
        sum+=d[i];
    sum/=(2*n);
    for(ll i=1;i<=n;i++)
        a[i]=(d[i]-sum)/n;
    if(ok()) print();
    else puts("-1");
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值