聽說前幾天在某博文立了個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;
}