好像以前寫過了,不過老師讓寫筆記就有寫了一遍
聲明本文是用筆記本電腦的鍵盤寫的誤觸的概率很是大因此可能會出現奇怪的問題算法
求兩數的最大公約數,證實我不會就不寫了spa
int gcd(int a, int b){ return b == 0? a : gcd(b, a % b); }
異或的性質ci
1.若\(a\ xor\ b=c\)則\(a\ xor \ c=b\)get
2.\(a-b<=a\ xor\ b\ \ \ \ \ (a>=b)\)string
設\(a=k_1*c,b=k_2*c\)it
則\(a-b=(k_1-k_2)*c\)io
則\(a-b>=c\)class
由於上述性質2
因此\(a-b<=c\)
因此只要枚舉\(a\)和\(c\),計算\(b=a-c\),則\(gcd(a,b)=gcd(a,a-c)=c\) 再驗證是否有\(c = a \ xor\ b\)便可,時間複雜度爲\(O(nlogn)\).
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<queue> #include<stack> #include<vector> #include<map> #include<string> #include<cstring> using namespace std; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } int n,t; const int N=30000001; int ans[N]; int main() { for(int c=1;c<=N;++c) { for(int a=c+c;a<=N;a+=c) { int b=a-c; if(c==(a^b)) ans[a]++; } } for(int i=2;i<=N;++i) ans[i]+=ans[i-1]; cin>>t; for(int i=1;i<=t;++i) { n=read(); cout<<"Case "<<i<<": "<<ans[n]<<'\n'; } return 0; }
擴展歐幾里得算法,簡稱 $ exgcd $,通常用來求解不定方程,求解線性同餘方程,求解模的逆元等。
方程以下
$ ax+by=gcd(a,b) $
\(gcd(a,b)=gcd(b,a\%b)\)
因此可設 \(a^,=b\ \ \ \ b^,=a \% b=a-a/b*b\)
即方程變爲 \(a^,x+b^,y=gcd(a,b)\)
\(b * x+(a-a/b * b) * y=gcd(a,b)\)
\(y * a+(x-y * a/b) * b=gcd(a,b)\)
因此咱們能夠在歐幾里得算法的基礎上加上參數\(x,y\)來求解線性同於方程
void exgcd(int a, int b, int& x, int& y) { if (b == 0) { x = 1, y = 0; return; } exgcd(b, a % b, y, x); y -= a / b * x; }
\(ax+cy=b\)的通解是\(x+k * c/gcd(a,c)\)
咱們令\(p=c/gcd(a,c)\)
因此最小的整數解就是\((x\%p+p)\%p\)
注意要先加上\(p\)再模\(p\)
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; ll A,B,C,k; inline void exgcd(ll a,ll b,ll &g,ll &x,ll &y){ if(b==0){x=1;y=0;g=a;} else{exgcd(b,a%b,g,y,x);y-=x*(a/b);} } int main(){ while(scanf("%lld%lld%lld%lld",&A,&B,&C,&k)!=EOF){ if(!A&&!B&&!C&&!k) break; ll c=B-A,a=C,b=1LL<<k,g,x,y; exgcd(a,b,g,x,y); if(c%g) printf("FOREVER\n"); else{ b/=g;c/=g; printf("%lld\n",(x%b*c%b+b)%b); } } }
不解釋了看圖吧
for(int i=2;i<=t;i++) { if(prime[i]) { for(int j=2 * i;j<MAXN , j += i) { prime[j]=false; } } }
仔細看上面的動圖你就會發現有些點被重複篩了
用一些辦法是每一個合數只會被它最小的質因子篩
用\(vis[i]\)記錄\(i\)的最小質因子
關於 if(i % prime[j] == 0) break
當 \(i\)是\(prime[j]\)的倍數時,\(i = k*prime[j]\),若是繼續運算 \(j+1,i * prime[j+1] = prime[j] * k prime[j+1]\),這裏\(prime[j]\)是最小的素因子,當\(i = k * prime[j+1]\)時會重複,因此才跳出循環。
舉個例子 :\(i = 8 ,j = 1,prime[j] = 2\),若是不跳出循環,\(prime[j+1] = 3,8 * 3 = 2 * 4 * 3 = 2 * 12\),在\(i = 12\)時會計算。由於歐拉篩法的原理即是經過最小素因子來消除。
for(int i=2; i<=n; i++){ if(!vis[i]) prime[cnt++]=i; for(int j = 0; j < cnt && i * prime[j] <= n; j++){ vis[i * prime[j]]=prime[j]; if(i % prime[j] == 0) break; } }