倍增

很久之前就学习过的算法,但是最近发现很多题目都可以用倍增的方法解决,并且十分好写,所以总结一下。

 

noip2013 货车运输

题目大意:n各点构成一个无向图,每条边有一个最大负载,有q辆货车,分别从不同的点到不同的点,求每辆货车最大运载量。

思路:为了满足最大负载,肯定是走图的最大生成树上的路径,并且答案就是每次路径上的最小边权。用倍增维护最小边权就可以了。

#include<iostream>



#include<cstdio>



#include<algorithm>



using namespace std;



struct use{



int xx,yy,va;



}r[50001];



int next[100001]={0},point[10001]={0},en[100001]={0},fa[10001]={0},f[10001][15]={0},



    g[10001][15]={0},dep[10001]={0},val[100001]={0};



int my_comp(const use &x,const use &y)



{



if (x.va>y.va) return 1;



else return 0;



}



int rool(int x)



{



if (fa[x]!=x) fa[x]=rool(fa[x]);



return fa[x];



}



void dfs(int x,int i)



{



int j,y;



dep[x]=i;



for (j=1;j<=13;++j)



{



f[x][j]=f[f[x][j-1]][j-1];



g[x][j]=min(g[f[x][j-1]][j-1],g[x][j-1]);



}



y=point[x];



while (y!=0)



{



if (dep[en[y]]==0)



{



f[en[y]][0]=x;



g[en[y]][0]=val[y];



dfs(en[y],i+1);



}



y=next[y];



}



}



int work(int x,int y)



{



int i,j,t,ans;



if (dep[x]>dep[y])



{



t=x;x=y;y=t;



}



ans=2100000000;



for (i=13;i>=0;--i)



{



if (dep[x]<=dep[f[y][i]])



{



ans=min(ans,g[y][i]);



y=f[y][i];



}



}



if (x==y) return ans;



for (i=13;i>=0;--i)



if (f[x][i]!=f[y][i])



{



ans=min(ans,min(g[x][i],g[y][i]));



x=f[x][i];y=f[y][i];



}



ans=min(ans,min(g[x][0],g[y][0]));



return ans;



}



int main()



{



int r1,r2,n,m,q,i,j,x,y,tot=0;



cin>>n>>m;



for (i=1;i<=m;++i)



  scanf("%d%d%d",&r[i].xx,&r[i].yy,&r[i].va);



sort(r+1,r+m+1,my_comp);



for (i=1;i<=n;++i)



{



  fa[i]=i;



  



    }



for (i=1;i<=m;++i)



{



    r1=rool(r[i].xx);



    r2=rool(r[i].yy);



    if (r1!=r2)



    {



        ++tot;



        next[tot]=point[r[i].xx];



        point[r[i].xx]=tot;



        en[tot]=r[i].yy;



        val[tot]=r[i].va;



        ++tot;



        next[tot]=point[r[i].yy];



        point[r[i].yy]=tot;



        en[tot]=r[i].xx;



        val[tot]=r[i].va;



        fa[r1]=r2;



    }



}



for (i=1;i<=n;++i)



  if (dep[i]==0)



    dfs(i,1);



cin>>q;



for (i=1;i<=q;++i)



{



scanf("%d%d",&x,&y);



if (rool(x)!=rool(y))



  printf("%d\n",-1);



else printf("%d\n",work(x,y));



}



}
View Code

 

水果姐逛水果街

题目大意:树上的一条路径,在使后面的权值减去前面的差最大。(变形1:可以修改权值;变形2:可以增加结点。)

思路:对于这种题目倍增或者链剖都可以解决。但是要十分注意信息(最大值、最小值、正着的最大差和反着的最大差)更新的顺序。(变形1:只能用链剖,可以很快的修改和查询;变形2:只能用倍增,因为倍增中,新加入一个点只需要用父亲的信息修改这个点的信息就可以了,不需要改动父亲,所以可以很快的求出。)

可是如果这两种变式结合在一起,该用什么呢?

#include<iostream>

#include<cstring>

#include<cstdio>

#include<algorithm>

#define maxnode 200005

#define inf 2100000000LL

using namespace std;

int fa[maxnode][20]={0},maxn[maxnode][20]={0},minn[maxnode][20]={0},zcha[maxnode][20]={0},tot=0,

    fcha[maxnode][20]={0},ai[maxnode]={0},point[maxnode]={0},next[maxnode*2]={0},en[maxnode*2]={0},

    dep[maxnode]={0};

struct use{

    int maxn,minn;

};

void add(int u,int v)

{

    ++tot;next[tot]=point[u];point[u]=tot;en[tot]=v;

    ++tot;next[tot]=point[v];point[v]=tot;en[tot]=u;

}

void ins(int u,int faa)

{

    int i;

    fa[u][0]=faa;dep[u]=dep[faa]+1;maxn[u][0]=max(ai[u],ai[faa]);minn[u][0]=min(ai[u],ai[faa]);

    zcha[u][0]=max(0,ai[faa]-ai[u]);fcha[u][0]=max(0,ai[u]-ai[faa]);

    for (i=1;i<=18;++i)

    {

        fa[u][i]=fa[fa[u][i-1]][i-1];

        zcha[u][i]=max(zcha[u][i-1],max(zcha[fa[u][i-1]][i-1],maxn[fa[u][i-1]][i-1]-minn[u][i-1]));

        fcha[u][i]=max(fcha[u][i-1],max(fcha[fa[u][i-1]][i-1],maxn[u][i-1]-minn[fa[u][i-1]][i-1]));

        maxn[u][i]=max(maxn[u][i-1],maxn[fa[u][i-1]][i-1]);

        minn[u][i]=min(minn[u][i-1],minn[fa[u][i-1]][i-1]);

    }

}

void dfs(int u,int faa)

{

    int i,j;

    ins(u,faa);

    for (i=point[u];i;i=next[i])

        if ((j=en[i])!=faa) dfs(j,u);

}

int ask(int u,int v)

{

    int i,j,ans=0;

    use ans1,ans2;

    ans1.maxn=ans1.minn=ai[u];ans2.maxn=ans2.minn=ai[v];

    if (dep[u]>dep[v])

    {

        for (i=18;i>=0;--i)

            if (dep[fa[u][i]]>=dep[v])

            {

                ans=max(ans,max(zcha[u][i],maxn[u][i]-ans1.minn));

                ans1.maxn=max(ans1.maxn,maxn[u][i]);

                ans1.minn=min(ans1.minn,minn[u][i]);

                u=fa[u][i];

            }

    }

    else

    {

        for (i=18;i>=0;--i)

            if (dep[fa[v][i]]>=dep[u])

            {

                ans=max(ans,max(fcha[v][i],ans2.maxn-minn[v][i]));

                ans2.maxn=max(ans2.maxn,maxn[v][i]);

                ans2.minn=min(ans2.minn,minn[v][i]);

                v=fa[v][i];

            }

    }

    if (u==v) return ans;

    for (i=18;i>=0;--i)

    {

        if (fa[u][i]!=fa[v][i])

        {

            ans=max(ans,max(zcha[u][i],max(maxn[u][i]-ans1.minn,

                max(fcha[v][i],ans2.maxn-minn[v][i]))));

            ans1.maxn=max(ans1.maxn,maxn[u][i]);

            ans1.minn=min(ans1.minn,minn[u][i]);

            ans2.maxn=max(ans2.maxn,maxn[v][i]);

            ans2.minn=min(ans2.minn,minn[v][i]);

            u=fa[u][i];v=fa[v][i];

        }

    }

    ans=max(ans,max(zcha[u][0],maxn[u][0]-ans1.minn));

    ans1.maxn=max(ans1.maxn,maxn[u][0]);

    ans1.minn=min(ans1.minn,minn[u][0]);

    ans=max(ans,max(fcha[v][0],ans2.maxn-ans1.minn));

    return ans;

}

int main()

{

    freopen("lct.in","r",stdin);

    freopen("lct.out","w",stdout);

    

    int n,m,i,j,q,ans=0,kk,u,v;

    scanf("%d",&n);

    for (i=1;i<=n;++i) scanf("%d",&ai[i]);

    for (i=1;i<n;++i)

    {

        scanf("%d%d",&u,&v);add(u,v);

    }

    dfs(1,0);scanf("%d",&m);

    for (i=1;i<=m;++i)

    {

        scanf("%d%d%d",&kk,&u,&v);

        if (kk==1)

        {

            u^=ans;v^=ans;ans=ask(u,v);printf("%d\n",ans);

        }

        else

        {

            v^=ans;ai[++n]=u;ins(n,v);

        }

    }

    

    fclose(stdin);

    fclose(stdout);

}
View Code

 

你可能感兴趣的:(倍增)