【AtCoder Regular Contest 092】D - Two Sequences(按位统计,归并)

原题链接
题意:给两个长度为n的序列Ai,Bi,求所有数(Ai + Bj)的异或和,i,j∈[0,n)。

被某大佬告知这是原题【hdu5270】,就去学习了下。
原始题解没找到,然后贴了份题解。
题解:
我们考虑两个数A,B。
为了描述方便,我们设[P]的值为:当表达式P的值为真时,[P]=1,否则[P]=0
我们现在考虑计算[(A+B)and(2i)>0]
首先我们将A,B都对2i+1取模,显然这样是不会影响答案的
则有一个十分显然的等式:
[(A+B)and(2i)>0]=[(A+B)≥(2i)]−[(A+B)≥(2i+1)]+[(A+B)≥(3∗2i)]
这个式子相当容易理解,这里不多述了
考虑每一位对答案的贡献是独立的,我们每一位分开做
于是现在问题变成了:给定数组A,B,求满足Ai+Bj≥limit的数对个数
我们可以将A,B排序后,直接O(n)计算即可
然而排序是O(nlogn)的,这样总复杂度就是O(nlognlogA)了,无法通过此题
于是这里有个小技巧
我们从高位往低位做,现在我们要实现的是:将A中每个数对P取模后将A排序
我们发现A会被分成两段,一段小于P,一段大于等于P,只有后面一段要取模,我们可以取模后直接将这两段归并,复杂度是O(n)的
时间复杂度:O(nlogA+nlogn)
参考:

#include <bits/stdc++.h>
using namespace std;

#define mem(a,n) memset(a,n,sizeof(a))
#define memc(a,b) memcpy(a,b,sizeof(b))
#define rep(i,a,n) for(int i=a;i<n;i++) ///[a,n)
#define pb push_back
#define mkp make_pair
#define mkt make_tuple
#define IO ios::sync_with_stdio(false)
#define fre freopen("in.txt","r",stdin)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
typedef unsigned long long ull;
const double PI=acos(-1.0);
const double E=2.718281828459045;
const double eps=1e-8;
const int INF=0x3f3f3f3f;
const ll inf=0x3f3f3f3f3f3f3f3f;
const int MOD=1e3;
const int N=1e2+5;
const ll maxn=2e5+5;
const int dir[4][2]= {-1,0,1,0,0,-1,0,1};
ll a[maxn],b[maxn],n;

void Sort(ll *a, ll md)
{
    int pos = n;
    for (int i = 0; i < n; i ++)
    {
        if (pos == n && a[i] >= md)
            pos = i;
        a[i] = a[i] & (md - 1);
    }
    inplace_merge(a, a + pos, a + n);
}
bool solve(ll limit)
{
    ll ans = 0;
    int that = n - 1;
    for (int i = 0; i < n; i ++)
    {
        while (that >= 0 && a[i] + b[that] >= limit)
            that --;
        ans += n - 1 - that;
    }
    return ans & 1;
}
inline ll Scan()
{
    ll r = 0,f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-')f = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9') {r = r * 10 + ch - '0';ch = getchar();}
    return r * f;
}

inline void Print(ll x)
{
    if(x<0){x=-x;putchar('-');}
    if(x>9) Print(x/10);
    putchar(x%10+'0');
}
int main()
{
    n=Scan();
    rep(i,0,n) a[i]=Scan();
    rep(i,0,n) b[i]=Scan();
    sort(a,a+n);
    sort(b,b+n);
    ll ans=0;
    for(int i=61; i>=0; i--)
    {
        Sort(a, 2LL<<i);
        Sort(b, 2LL<<i);
        ll res=solve(1LL<<i)^solve(2LL<<i) ^solve(3LL<<i);
        ans|=res<<i;
    }
    Print(ans);
    return 0;
}

另写下一份,容易想到的是二进制异或,拆分成位。
通过进位使第k位+1的数对必须满足 ( A[i] & ((1<<k)-1) ) + ( B[i] & ((1<<k)-1) ) >= (1<<k)。
首先预处理cx[k][i]=B[i] & ((1<<k)-1),然后对所有cx[k]排序。(如果是累加预处理的话先全部处理出来再排序)

枚举数位k,然后枚举A[i] & ((1<<k)-1),在cx[k-1]中二分出满足要求的数对个数%2后累加进答案。

参考:https://www.cnblogs.com/onioncyc/p/8593165.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值