树状数组笔记 + 矩阵快速幂笔记

树状数组先认识一个很有意思的操作,x & (- x)其实是,x & (~x | 1)如果x为偶数,就取最后的零的个数的2次幂,如果x为奇数,取1
先画个图
树状数组笔记 + 矩阵快速幂笔记_第1张图片
此图从https://blog.csdn.net/Small_Orange_glory/article/details/81290634转
由此可见,我们用树状数组可以表示任何一个区间的和
我们在这边主讲用树状数组求逆序对,首先先用到离散化,不然很麻烦,把大小全部转为序号
每次输入一个数,只要没超过n,就一直加上x & (-x)让c[x] ++表示只要在x之上的都比它大
然后检验,向下,找到之前比它小的,然后加上1表示自己,然后用i去减(除了比它小的就只有比它大的了)

然后是矩阵快速幂
首先我们认识到一个操作,c[i][j] = a[i][k] * a[k][j](k = 1;k <= n;k ++)
这是简单的矩阵乘法,难点在于构造矩阵,用快速幂可以加速矩阵,平时的快速幂只是一个数,现在变成了一个矩阵
就酱
以斐波那契为例
见代码

// luogu-judger-enable-o2
#include
#include
#include
using namespace std;
const int Mod = 1000000007;
struct Matrix{
    long long a[3][3];
    Matrix () 
    {
        memset(a,0,sizeof(a));
    };
    Matrix operator*(const Matrix &b) const
    {
        Matrix res;
        for(int i = 1;i <= 2;i ++)
            for(int j = 1;j <= 2;j ++)
                for(int k = 1;k <= 2;k ++)
                    res.a[i][j] =  (res.a[i][j] + a[i][k] * b.a[k][j]) % Mod;
        return res;
    }
}base,ans;
void init()
{
    base.a[1][1] = 1;
    base.a[1][2] = 1;
    base.a[2][1] = 1;
    ans.a[1][1] = 1;
    ans.a[1][2] = 1;
}
void KSM(long long n)
{
    while(n)
    {
        if(n & 1)
            ans = ans * base;
        base = base * base;
        n = n >> 1; 
    }
    return;
}
int main()
{
    long long n;
    scanf("%lld",&n);
    if(n <= 2)
    {
        cout << 1;
        return 0;
    }
    init();
    KSM(n - 2);
    printf("%lld",ans.a[1][1] % Mod);
    return 0;
}

你可能感兴趣的:(树状数组笔记 + 矩阵快速幂笔记)