1. 程式人生 > >UVA - 12716 - 異或序列

UVA - 12716 - 異或序列

 求滿足GCD(a,b) = a XOR b; 其中1<=b <=a<=n。  

首先做這道題需要知道幾個定理:

  異或:a XOR b = c 那麼 a XOR c = b;

   那麼我們令GCD(a,b)= c; 這樣 a 是  c  倍數。我們可以通過遍歷c , 然後通過篩法,把c的倍數晒出當作a。求b如何求呢?

   書上提供一種方法是利用a XOR c=b 用 gcd(a,b)=c 驗證。但是這個方法是超時的,gcd是logn 級別的 總的時間複雜度,n*(logn)^2。這是我們不能接受的。

  還有一種方法是證明b=a-c。這個證明還是不容易的 :

      首先gcd(a,b)=c<=a-b 其次要證明a^b>=a-b  但是這是不容易。我們可以這樣想,他們什麼時候取等於,我們知道異或是相同為0,不同為1 那麼 我們把a,b用二進位制展開,這樣我們a,b是每一位對應的,我們把所以不同的二進位制位,全部變為ai = 1,bi = 0。這樣我們知道,沒有借位 那麼a^b == a-b 。那麼後面按照XOR序列的順序變化,a-b變小,那麼a^b>=a-b 從而 c=a-b。命題得證。

   證明是否成立,直接用a^b==a-b即可。可以的把,保持在f數組裡面,f[i]代表a=i時的情況數。最後求一個字首和既可以,最後o1查詢即可。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define rep(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
const int maxx=30000001;
int f[maxx];
void init(){
    for (int i=1;i<=maxx/2;i++){
        for (int j=2*i;j<=maxx;j+=i){
           
int b=j-i; if ((j^b)==i){ f[j]++; } } } for (int i=2;i<=maxx;i++){ f[i]+=f[i-1]; } } int main(){ int t; int n; int zz=0; scanf("%d",&t); int cnt=0; int a; memset(f,0,sizeof(f)); init(); while(t--){ zz++; scanf("%d",&n); printf("Case %d: %d\n",zz,f[n]); } return 0; }