UVA-12716 GCD+异或运算性质+约数筛法

不想你离开。 提交于 2020-02-27 11:07:07

GCD XOR
Description:
输入一个整数n,有多少对整数(a,b)满足 1≤b≤a≤n,gcd(a,b)==aXORbgcd(a,b)==a XOR b
思路历程:
①暴力循环 时间复杂度O(n^2),毫无疑问会超时。

②考虑到(虽然自己一开始不知道)
aXORb=c,thenaXORb=c,then,aXORc=baXORc=b
令c=gcd(a,b),或者 c 必然是a的约数,所以枚举a,c,用 a XOR c算出b,再检验 gcd(a,b)==c。时间复杂度O(nlognlogn
ps:一开始还想着用vector<int> facotr[] 去存储枚举的a的正约数集合,发现编译都通不过(数组太大了)
但是,这样还是TLE。还是得优化。


gcd(a,b)abaXORba>bgcd(a,b)≤a-b≤aXORb\qquad a>b
gcd(a,b)=aXORb=cgcd(a,b)=aXORb=c
由上面的式子得,ab=ca-b=c,于是还是枚举a,c,用a-b=c算出b,再检验
a XOR b == c。
时间复杂度:O(n*logn)

int t,n;
int ncase=0;
int main(){
    cin>>t;
    while(t--)
    {
        ll ans=0;
        scanf("%d",&n);
        int a,b,c;
        for(c=1;c<=n;c++)
        {
            for(int k=2;k<=n/c;k++)
            {
                
                a=k*c;
                b=a-c;
                if(c==(a^b))
                {
                    ans++;
                }
            }
        }
        printf("Case %d: %lld\n",++ncase,ans);
    }
    return 0;
}

但是,还是TLE!
题目要求测试量可达10000,整体时间复杂度O(Tnlogn)

④依照用空间换时间的想法。
时间复杂度:O(n*logn+T)

#include<stdio.h>
#include<iostream>
#include<cmath>
#include<math.h>
#include<string>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
#define ms0(a) memset(a,0,sizeof(a))
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 3e7;
int t,n;
int ncase=0,num[maxn+10];
void init()
{
    for(int c=1;c<=maxn/2;c++)
    {
        for(int a=2*c;a<=maxn;a+=c)
        {
            if((a^(a-c))==c)
                num[a]++;
        }
    }
    for(int i=2;i<=maxn;i++)
        num[i]+=num[i-1];
}
int main(){
    cin>>t;
    init();
    while(t--)
    {
        scanf("%d",&n);
        printf("Case %d: %d\n",++ncase,num[n]);
    }
    return 0;
}

num[n] 一开始存放的是在 a=n 时,满足1≤b≤a 且满足gcd(a,b)==aXORbgcd(a,b)==a XOR b的数对的对数。
当然题目要求的是小于某个数n的数对的个数,也就说
num[n]=k=1nnum[k]num[n]=\sum_{k=1}^n num[k]

ps:用倍数法枚举正约数

    for(int c=1;c<=maxn/2;c++)
    {
        for(int a=2*c;a<=maxn;a+=c)
        {
        //...... c是a的约数
        }
    }

或者

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n/i;j++)
        {
        //......  i是i*j 的约数
        }
    }
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!