知识点补档2

Fib数列性质

f ( n ) = 5 5 ( ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ) f(n)=\frac{\sqrt 5}{5}((\frac{1+\sqrt 5}{2})^n-(\frac{1-\sqrt 5}{2})^n) f(n)=55 ((21+5 )n(215 )n)

∑ i = 1 n f ( i ) = f ( n + 2 ) − 1 \sum_{i=1}^n f(i)=f(n+2)-1 i=1nf(i)=f(n+2)1

∑ i = 1 n f ( i ) 2 = f ( n ) ∗ f ( n + 1 ) \sum_{i=1}^n f(i)^2=f(n)*f(n+1) i=1nf(i)2=f(n)f(n+1)

∑ i = 1 n i f ( i ) = n ∗ f ( n + 2 ) − f ( n + 3 ) + 2 \sum_{i=1}^n if(i)=n*f(n+2)-f(n+3)+2 i=1nif(i)=nf(n+2)f(n+3)+2

∑ i = 1 n f ( 2 i − 1 ) = f ( 2 n ) − f ( 2 ) + f ( 1 ) \sum_{i=1}^n f(2i-1)=f(2n)-f(2)+f(1) i=1nf(2i1)=f(2n)f(2)+f(1)

∑ i = 1 n f ( 2 i ) = f ( 2 n + 1 ) − f ( 1 ) \sum_{i=1}^n f(2i)=f(2n+1)-f(1) i=1nf(2i)=f(2n+1)f(1)

g c d ( f [ i ] , f [ i + 1 ] ) = 1 gcd(f[i],f[i+1])=1 gcd(f[i],f[i+1])=1

f [ m + n ] = f [ m − 1 ] f [ n ] + f [ m ] f [ n + 1 ] f[m+n]=f[m-1]f[n]+f[m]f[n+1] f[m+n]=f[m1]f[n]+f[m]f[n+1]

g c d ( f [ n + m ] , f [ n ] ) = g c d ( f [ n ] , f [ m ] ) gcd(f[n+m],f[n])=gcd(f[n],f[m]) gcd(f[n+m],f[n])=gcd(f[n],f[m])

g c d ( f [ n ] , f [ n + m ] ) = f [ g c d ( n , n + m ) ] gcd(f[n],f[n+m])=f[gcd(n,n+m)] gcd(f[n],f[n+m])=f[gcd(n,n+m)]

线性基

线性基是构造出一组序列 p 1 , p 2 , … , p n p_1,p_2, \ldots , p_n p1,p2,,pn,使得从这些数字中任选一个子集的异或和的值域同等于从原序列中任选一个子集的异或和的值域。同时保证 p i p_i pi在二进制下的最高位是 2 i 2^i 2i。这样求异或和最大的时候就可以按位来贪心。

构造方法:对于一个需要插入线性基的数 x x x,首先找到它在二进制位下的最高位 i i i,检查 p i p_i pi是否已经生成。若尚未生成,则令 p i = x p_i=x pi=x并结束操作,否则令 x = x    x o r    p i x = x \; xor \; p_i x=xxorpi,并继续插入操作。

//给定n个整数(数字可能重复),求在这些数中选取任意个,使得他们的异或和最大。
#include 
using namespace std;

typedef long long ll;

struct Node{
    ll p[70];
    void insert(ll x) {
        for (int j = 63; j >= 0; --j) {
            if (!(x>>j)) continue;
            if (!p[j]) {p[j]=x;return;}
            x^=p[j];
        }
    }
    ll query(ll x) {
        for (int j = 63; j >= 0; --j)
            x = max(x, x^p[j]);
        return x;
    }
}S;

int main()
{
    int n;
    ll x;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%lld", &x);
        S.insert(x);
    }
    cout << S.query(0) << '\n';
    return 0;
} 

其中 q u e r y ( x ) query(x) query(x)是查询异或上 x x x后的最大值,如果要直接查最大值的话直接 q u e r y ( 0 ) query(0) query(0)就好了。

两个线性基可以暴力合并,把其中一个直接插入到另一个中即可,复杂度为 O ( log ⁡ 2 n ) O(\log^2n) O(log2n)

线性基还可以查询第 k k k小,需要重构一下原有的线性基,保证:线性基中某一个元素的非最高位为1当且仅当这个位置对应的线性基元素不存在。

//多组样例,给定n个数,询问这n个数第k小的异或值
#include 
using namespace std;
#define ll long long

struct xxj{
    ll p[65],q[65];int fg,cnt;
    void init() {
        memset(p, 0, sizeof(p));
        fg = cnt = 0;
    }
    void insert(ll x) {
        for (int i = 62; ~i; --i) {
            if (!(x>>i)) continue;
            if (!p[i]) {
                p[i] = x;
                return;
            }
            x ^= p[i];
        }
        fg = 1;
    }
    void rebuild() {
        for (int i = 62; ~i; --i)
            for (int j = i-1; ~j; --j)
                if ( p[i] &(1ll << j) ) p[i] ^= p[j];
        for (int i = 0; i <= 62; ++i) 
            if (p[i]) q[cnt++] = p[i];
    }
    ll query(ll k) {
        if (fg) --k;
        if (k >= (1ll << cnt)) return -1;
        ll res = 0;
        for (int i = cnt-1; ~i; --i)
            if (k & (1ll << i)) res ^= q[i];
        return res;
    }
}S;

int main()
{
    int T, n, k;
    ll x;
    cin >> T;
    for (int i = 1; i <= T; ++i) {
        S.init();
        scanf("%d", &n);
        while (n--) S.insert(x);
        S.rebuild();
        scanf("%d", k);
        cout << S.query(k) << '\n';
    }
    return 0;

}

你可能感兴趣的:(数论)