• 杂项算法小总结


    前言

    很多知识不好整理,这篇文章总结一些杂乱的东西,涉及的内容有,( exttt{RMQ}),矩阵乘法(本来应该写在代数里面的,但我的代数知识少得可怜,就写在杂项里面了),高斯消元,( exttt{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
    ",ask(i-b[i]+1,i));
    	return 0;
    }
    

    矩阵乘法

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

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

    设有矩阵

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

    设有另一个矩阵

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

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

    [C_{2,2}= egin{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
    ");
    			continue;
    		}
    		node ans=pow(p);
    		printf("%lld
    ",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("
    ");		
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    给我30000出租车,还你一个不堵车的北京
    使用vim代替IDE
    (转)声明,函数与函数指针
    想恶作剧的请看过来
    bash命令提示符的更改
    (转)微软面试
    140个Google面试问题
    UTF8 GBK UTF8 GB2312 之间的区别和关系(转)
    MyBooksReadingStatic
    让SlickEdit 自动编译Keil C51工程
  • 原文地址:https://www.cnblogs.com/valentino/p/11838515.html
Copyright © 2020-2023  润新知