Codeforces Round #633(Div.2) E. Perfect Triples

E. Perfect Triples

题目链接

题目概要:

有一个无限大的数组\(s\)。(开始为空)
每次找到字典序最小的三个数\((a,b,c)\)满足:

  • \(a\oplus b\oplus c=0\).
  • \(a,b,c\notin s\).
    \(t\)次询问,每次问数组第\(n\)项是多少。

思路:

打表后我们发现,三元组的第一项始终是 \(4^x\)后一段连续的数。这样我们尝试在二进制下两位两位的转移
(也就是四进制,为了使异或看起来方便我们这样描述):
每两位可以取的数值是\(00,01,10,11\)
首先假设已经用完了\(1 \to 4^n-1\)的数,现在考虑\(4^n \to 4^{n+1}-1\)
要得到 \(a<b<c\) ,最高的两位只能是\(01,10,11\),也就是说\(a,b,c\)最高的两位互不相同。
现在前两位已经知道了,我们继续往下看。
我们先把样例给出的几项(二进制)用下表的形式展示:

\(a\) \(b\) \(a\oplus b\)
\(00\) \(00\) \(00\)
\(01\) \(10\) \(11\)
\(10\) \(11\) \(01\)
\(11\) \(01\) \(10\)
我们大胆猜测,(以两位为单位)在四进制下,所有的数值都满足以上条件。
  • 两位的情况下\((a,b,c)\)只有\(1,2,3\),显然满足。
  • 假设\(2k\)位的情况下\(a,b,c\)都满足,验证\(2k+2\)位:
    我们先把这\(2k\)位左移两位(设其中任意一对数值是\(pa,pb,pc\),显然\(pa \oplus pb=pc\)),在字典序最小的情况下我们首先考虑\(a\)
    \(00\):显然\(pa+00,pb+00,pc+00\)成立。
    \(01\):对于\(b\)\(00,01\)都不成立(\(b,c\)出现重复),此时应为 \(pa+01,pb+10,pc+11\)
    ...
    剩下的手推便会发现的确满足上表。

这样我们只要确定第\(n\)个数所属的三元组\((a,b,c)\)\(a\)即可确定这个值。

代码:

#include "iostream"
#include "stdio.h"
#include "string.h"
#include "algorithm"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int N=1e6+5;
const ll mod=998244353;
const double eps=1e-5;
//const double pi=acos(-1);

#define ls p<<1
#define rs p<<1|1
ll f[3][4]={{0,3,1,2},{},{0,2,3,1}};
void solve(ll x,int y)
{
    if(y==1)
    {
        printf("%lld\n",x);
        return;
    }
    ll ans=0,p=1;
    while(x)
    {
        ans=ans+f[y][x%4]*p;
        x>>=2;
        p<<=2;
    }
    printf("%lld\n",ans);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    int _;
    ll n;
    cin>>_;
    while(_--)
    {
        cin>>n;
        ll j=1,a;
        while(j<=n) j<<=2;
        j>>=2;
        if(j+2>=n) a=j;
        else a=j+(n-j)/3;
        solve(a,n%3);
    }
    return 0;
}
posted @ 2020-04-13 18:15  Suiyue_Li  阅读(295)  评论(0编辑  收藏  举报