树状数组先认识一个很有意思的操作,x & (- x)其实是,x & (~x | 1)如果x为偶数,就取最后的零的个数的2次幂,如果x为奇数,取1
先画个图
此图从https://blog.youkuaiyun.com/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<iostream>
#include<cstdio>
#include<cstring>
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;
}