杂项算法小总结

前言

很多知识不好整理,这篇文章总结一些杂乱的东西,涉及的内容有,\(\texttt{RMQ}\),矩阵乘法(本来应该写在代数里面的,但我的代数知识少得可怜,就写在杂项里面了),高斯消元,\(\texttt{2-SAT}\),扫描线,随机化与模拟退火(还没学,学了会写的)。

RMQ

玲珑杯 Round15 G咸鱼拷问

\(RMQ\),即区间最值询问,是一类查找区间最值的问题,这里讲一下比较有名的在线算法\(ST表\)

\(ST\)表,本质上是一种\(dp\),设\(f[i][j]\)表示序列中第\(i\)个数开始向后\(2^j\)个数这个区间的最值,那么初始状态\(f[i][0]=a[i]\)\(dp\)方程为:

\(f[i][j]=max/min(f[i][j-1],f[i+(1<<(j-1))][j-1])\)

从中我们可以看出,从\(i\)开始后的\(2^j\)个数,我们把它分成了两段,一段是第\(i\)个数到第\(i+(1<<(j-1))-1\)个数,一段是第\(i+(1<<(j-1))\)个数到第\(i+(1<<j)-1\)个数,也就是这中间的\(2^j\)个数。
以上是预处理部分,现在如果我们要考虑查询\([l,r]\)这个区间呢?看图(来自某\(C\)姓教练)

在此,\(x_{max}=log(r-l+1)/log(2)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,a[100005],b[100005],maxn[100005][25],minn[100005][25];
void rmq()
{
	for(int j=1;j<20;j++)
		for(int i=1;i<=n;i++)
		if(i+(1<<j)-1<=n)
		maxn[i][j]=max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]),minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);
}
int ask(int l,int r)
{
	int x=log(r-l+1)/log(2);
	int maxx,minx;
	maxx=max(maxn[l][x],maxn[r-(1<<x)+1][x]);
	minx=min(minn[l][x],minn[r-(1<<x)+1][x]);
	return maxx*minx;
}
signed main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	scanf("%lld",&a[i]),maxn[i][0]=minn[i][0]=a[i];
	for(int i=1;i<=n;i++)
	scanf("%lld",&b[i]);
	rmq();
	for(int i=1;i<=n;i++)
	printf("%lld\n",ask(i-b[i]+1,i));
	return 0;
}

矩阵乘法

矩阵乘法是代数的中的知识,两个矩阵相乘的结果自然也是矩阵,那么它的运算法则是什么呢?

设一个\(m*n\)的矩阵为\(A\),一个\(p*m\)的矩阵为\(B\),那么这两个矩阵的乘积矩阵\(C\)的大小就应该是\(m*m\)

设有矩阵

\[A_{2,3}= \begin{pmatrix} a_{1,1} & a_{1,2} & a_{1,3} \\ a_{2,1} & a_{2,2} & a_{2,3} \end{pmatrix} \]

设有另一个矩阵

\[B_{3,2}= \begin{pmatrix} b_{1,1} & b_{1,2} \\ b_{2,1} & b_{2,2} \\ b_{3,1} & b_{3,2} \end{pmatrix} \]

那么两者相乘的结果就应该是

\[C_{2,2}= \begin{pmatrix} a_{1,1}*b_{1,1}+a_{1,2}*b_{2,1}+a_{1,3}*b_{3,1} & a_{1,1}*b_{1,2}+a_{1,2}*b_{2,2}+a_{1,3}*b_{3,2} \\ a_{2,1}*b_{1,1}+a_{2,2}*b_{2,1}+a_{2,3}*b_{3,1} & a_{2,1}*b_{1,2}+a_{2,2}*b_{2,2}+a_{2,3}*b_{3,2} \end{pmatrix} \]

也就是说,矩阵之间的乘法是第一个矩阵的行乘第二个矩阵的列。
根据矩阵的运算法则,我们不难看出,矩阵是满足乘法分配律和乘法结合律的,但是矩阵不满足乘法交换律,一旦交换,结果矩阵就会产生变化。

矩阵乘法在\(OI\)中主要运用于优化递推式,有的时候\(O(n)\)的复杂度也许会高达\(1e9\)甚至\(1e12\),这个时候如果有矩阵乘法(严格来说是矩阵快速幂)的优化,复杂度就可以降为\(O(logn)\),可以接受。
只要我们把递推式写了出来,矩阵一般都比较好构造。
矩阵加速

#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
const ll mod=1e9+7;
struct node
{
	ll m[3][3];
//	node(){memset(m,0,sizeof m);}
}u,v;
//u.m[3][3]=
//{
//	{0,1,0},
//	{0,1,0},
//	{0,1,0}
//};
//v.m[3][3]=
//{
//	{1,0,1},
//	{1,0,0},
//	{0,1,0}
//};
void init()
{
	memset(u.m,0,sizeof(u.m));
	memset(v.m,0,sizeof(v.m));
	u.m[0][1]=u.m[1][1]=u.m[2][1]=1;
	v.m[0][0]=v.m[1][0]=v.m[0][2]=v.m[2][1]=1;
}
node mul(node a,node b)
{
	node t;
	for(int i=0;i<3;i++)	
		for(int j=0;j<3;j++)
		{
			t.m[i][j]=0;
			for(int k=0;k<3;k++)
			t.m[i][j]=(t.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
		}
	return t;
}
node pow(ll b)
{
	while(b)
	{
		if(b&1) u=mul(u,v);
		v=mul(v,v);
		b>>=1;
	}
	return u;
}
ll T;
int main()
{
	scanf("%lld",&T);
	while(T--)
	{
		init();
		ll p;
		scanf("%lld",&p);
		if(p<=3)
		{
			printf("1\n");
			continue;
		}
		node ans=pow(p);
		printf("%lld\n",u.m[1][0]);
	}
	return 0;
	
}

矩阵快速幂

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
const int mod=1e9+7;
ll n,k;
struct node
{
	ll m[105][105];
}a;
node mul(node a,node b)
{
	node t;
//	memset(t,0,sizeof(t));
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
		t.m[i][j]=0;
		for(int k=1;k<=n;k++)
		t.m[i][j]=(t.m[i][j]+a.m[i][k]*b.m[k][j])%mod;			
		}
	
	return t;
}
node pow(node a,ll k)
{
	node ans=a,b=a;
	k--;
//	memset(ans,0,sizeof(ans));
	while(k)
	{
		if(k&1) ans=mul(b,ans);
		b=mul(b,b);
		k>>=1;
	}
	return ans;
}
int main()
{
	scanf("%lld%lld",&n,&k);
	for(ll i=1;i<=n;i++)
		for(ll j=1;j<=n;j++)
		scanf("%lld",&a.m[i][j]);
	a=pow(a,k);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		printf("%lld ",a.m[i][j]);
		printf("\n");		
	}

	return 0;
}
posted @ 2019-11-11 21:12  haruka酱  阅读(299)  评论(1编辑  收藏  举报
浏览器标题切换
浏览器标题切换end