听说前几天在某博文立了个flag,说要在北京补博客…果然不要乱立flag啊!【此处省去一万个flag】
- RMQ问题(Range Minimum/Maximum Query)
RMQ问题是指对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。【来自百度百科】
一般情况下 RMQ问题可以用ST表解决(这里的一般情况是指没有修改操作,只进行查询)首先用dp进行预处理,通过进行合并两个区间的信息,来得知大的区间的信息。每次可以O(1)查询。
我们设f[i][j]为从i开始连续的2^j次方个数中的最大值。f[i][0]则为从i开始的2^0次方个数,也就是从i开始1个数的最大值,也就是a[i]。这里要进行初始化。接下来是状态转移方程。我们把这个大区间分为两个区间(这个区间的长度肯定为偶数)。一段为i到i+2^(j-1)-1,一段为i+2^(j-1)到i+2^j-1(这两段的长度都为2^(j-1))。所以我们就有了这样的一个状态转移方程:f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1])。在查询的时候,我们设p等于以二为底r-l+1的对数,把区间分为两个区间完全覆盖。举例说明,要求区间[2,8]的最大值,就要把它分成[2,5]和[5,8]两个区间,因为这两个区间的最大值我们可以直接由f[2][2]和f[5][2]得到。
有个图可以直观的感受一下:
有个裸题是poj 3264 Balanced Lineup 链接
下面是代码(代码当中的k就是转移方程的i,i就是转移方程的j,j则是方便写代码的):
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int MAXN=50000+10;
int f[MAXN][20],g[MAXN][20];
int a[MAXN];
int main()
{
int n,q;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
f[i][0]=a[i];
g[i][0]=a[i];
}
for(int i=1,j=2;j<=n;i++,j*=2)
{
for(int k=1;k+j-1<=n;k++)
{
f[k][i]=max(f[k][i-1],f[k+(j>>1)][i-1]);
g[k][i]=min(g[k][i-1],g[k+(j>>1)][i-1]);
}
}
while(q--)
{
int l,r;
scanf("%d%d",&l,&r);
int p=int(log(r-l+1)/log(2));
int maxx=max(f[l][p],f[r-(1<<p)+1][p]);
int minn=min(g[l][p],g[r-(1<<p)+1][p]);
printf("%d\n",maxx-minn);
}
return 0;
}
- LCA(Lowest Common Ancestors) 最近公共祖先
之前只会暴力的LCA,现在终于会了倍增版的。说句实话,打次的倍增不如暴力啊!有图为证!
【其实是自己太弱了】
其实倍增的LCA就是先对于树上的每个点用dfs序进行编号。然后在树上做好st表,但个人认为这个不如st表好理解QAQ。
我们设father[i][j]表示从i号点2^j倍的祖先,则father[i][0]即为节点i的父亲。
则father[i][j]=father[father[i][j-1]][j-1](从i跳到2^(j-1)倍,再从这个点网上跳2^(j-1)倍)
我最初学倍增的时候总会想会不会跳过去啊…其实后来发现,当它跳到它们的祖先,快要跳到祖先的时候,就会一步一步的跳,也就是father[i][0]。这样就会保证不会跳出去了。
poj 1330 Nearest Common Ancestors是个裸题【从名字就可以看出来233】链接
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=100000+50;
struct edge{
int from,to;
}es[MAXN<<1];
int first[MAXN],next[MAXN],deep[MAXN],fa[MAXN];
int tot;
int father[MAXN][30];
void init()
{
memset(first,-1,sizeof(first));
memset(deep,0,sizeof(deep));
memset(fa,0,sizeof(fa));
tot=0;
}
void build(int ff,int tt)
{
es[++tot]=(edge){ff,tt};
next[tot]=first[ff];
first[ff]=tot;
}
void dfs(int u, int f)
{
deep[u]=deep[f]+1;
fa[u]=f;
father[u][0]=f;
for(int i=1;i<=24;i++)
father[u][i]=father[father[u][i-1]][i-1];
for(int i=first[u];i!=-1;i=next[i])
{
int v=es[i].to;
if(v==f) continue;
dfs(v,u);
}
}
int lca(int x,int y)
{
if(deep[x]<deep[y])
swap(x,y);
if(deep[x]>deep[y])
{
int t=deep[x]-deep[y];
for(int i=24;i>=0;i--)
{
if(t&(1<<i)) //如果t的第i位=1 就跳这些步
x=father[x][i];
}
}
if(x!=y)
{
for(int i=24;i>=0;i--)
{
if(father[x][i]!=father[y][i])
{
x=father[x][i];
y=father[y][i];
}
}
}
else return x;
return father[x][0];
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,f,t;
scanf("%d",&n);
init();
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&f,&t);
build(f,t);
}
for(int i=1;i<=n;i++)
{
if(!deep[i])
dfs(i,i);
}
int x,y;
scanf("%d%d",&x,&y);
cout<<lca(x,y)<<endl;
}
return 0;
}
/*
1
16
1 14
8 5
10 16
5 9
4 6
8 4
4 10
1 13
6 15
10 11
6 7
10 2
16 3
8 1
16 12
12 7
4
*/
/*
5
1 2
2 3
2 4
4 5
*/
- 快速幂
非递归版的快速幂就是通过倍增实现的,我比较习惯打非递归版的,代码比较短而且给我一种很快的错觉233。直接上代码!(此代码基于codevs 1497)(此题数据比较水,代码比较naive,取膜取少了,注意要多取膜!)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
LL fast_pow(LL a,LL p,LL m)
{
LL ans=1;
for(;p;p>>=1,a=(a*a)%m)
if(p&1)
ans=(ans*a)%m;
return ans;
}
int main()
{
LL x,y,mod;
scanf("%lld%lld%lld",&x,&y,&mod);
cout<<x<<"^"<<y<<" "<<"mod"<<" "<<mod<<"=";
printf("%lld",fast_pow(x,y,mod));
return 0;
}