倍增专题

Posted sjsjsj-minus-si

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了倍增专题相关的知识,希望对你有一定的参考价值。

【map】P4889 kls与flag

可以发现这道题求的是

assume j>i

j?i=h[i]+h[j]

j?i=abs(h[i]=h[j])

的对数。

那么显然,因为高度大于0,所以一个数对不可能同时满足两条式子,所以可以==分开算==。

那么进行分类讨论:

  • (1) j?i=hi+hj -->hi+i=j?hj,那么开一个map,存下所有值的个数,每次都更新答案
  • (2) j?i=hj?hi, -->j?hj=i?hi,同(1)
  • (3) j?i=hi?hj, -->hi+i=hj+j,同(1)

那么如何保证j>i呢?==倒序==不就好了吗!

const int N=2e5+10;
int h[N];
int n,m;
map<int,int>mp;
#define big long long
big ans;

int main(){
    rd(n),rd(m);
    rep(i,1,n)rd(h[i]);
    //情况1
    dwn(i,n,1){
        ans+=(big)mp[h[i]+i];
        mp[i-h[i]]++;
    }
    mp.clear();
    //情况3
    dwn(i,n,1){
        ans+=(big)mp[i+h[i]];
        mp[h[i]+i]++;
    }
    mp.clear();
    //情况2
    dwn(i,n,1){
        ans+=(big)mp[i-h[i]];
        mp[i-h[i]]++;
    }
    printf("%lld",ans);
    return 0;
}

【倍增】P1613 跑路

先用一个数组预处理出可以一步到达的点对,再上floyd(n<=50不上floyd上什么)

BTW:部分盆友的智商堪忧,maxlongint 就是maxint 啊,你们开什么64,明明31就行了。?

const int N=60;
int n,m;
int dis[N][N];
bool G[N][N][64+5];

inline void prework(){
    rep(logn,1,64)
        rep(k,1,n)
            rep(i,1,n)
                rep(j,1,n)
                    if(G[i][k][logn-1] && G[k][j][logn-1]){
                        G[i][j][logn]=1;
                        dis[i][j]=1;
                    }
}

inline void floyd(){
    rep(k,1,n)
        rep(i,1,n)
            rep(j,1,n)
                dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}

int main(){
    #ifdef WIN32
    freopen("a.txt","r",stdin);
    #endif
    rd(n),rd(m);
    mem(dis,0x3f);
    while(m--){
        int u,v;rd(u),rd(v);
        G[u][v][0]=1;
        dis[u][v]=1;
    }
    prework();
    floyd();
    printf("%d",dis[1][n]);
    return 0;
}

【倍增】#137. 关联点

思考过程:

每个点网上跳val[i]步,到达的节点就是它产生贡献的节点,可是怎么判断是左边产生的贡献还是右边产生的贡献呢?很简单,我们只需条val[i]-1步,判断当前节点是它父节点的左儿子还是右儿子就好。

bebug:lson 和rson 不为0的时候才记录啊,我真是个智娃。

const int N=2e5+10;
int n,m;
int val[N],f[N][18+2],son[N][2],ansl[N],ansr[N];

struct edge{
    int v,next;
}e[N<<1];

int head[N],edge_num;
inline void adde(int u,int v){
    e[++edge_num].v=v;
    e[edge_num].next=head[u];
    head[u]=edge_num;
}

int main(){
    rd(n);
    rep(i,1,n){
        rd(val[i]);val[i]--;//开始就自减一
    }
    rep(i,1,n){
        int lson,rson;
        rd(lson),rd(rson);
        if(lson)f[lson][0]=i,son[i][0]=lson;
        if(rson)f[rson][0]=i,son[i][1]=rson;
    }
    rep(i,0,18)
        rep(u,1,n)
            f[u][i+1]=f[f[u][i]][i];
    rep(u,1,n){
        int x=u;//因为u要一直往上跳,而u还在循环里,不能直接改变,所以多开一个变量
        dwn(i,18,0)
            if((1<<i) & val[u])x=f[x][i];//注意这里是&val[u] 而不是一直在跳的val[x]
        int fa=f[x][0];
        if(son[fa][0]==x)ansl[fa]++;
        else ansr[fa]++;
    }
    rep(i,1,n)
        printf("%d %d
",ansl[i],ansr[i]);
    return 0;
}

【倍增】P1967 货车运输

SOL

首先便是想到了Floyd的暴力方法,状态转移方程也不难推出:w[i] [j]=min(w[i] [j], w[i] [k]+w[k] [j]);但是n^ 3次方时间复杂度和n^2的空间复杂度是显然不可取的。

于是我们思考,可以发现有一些权值较小的边是不会被走过的。正如样例中的第三条边,就算有其他的很多条边,这条边无论如何也是不会被走过的。于是我们想到了可以将图中这样的边去掉,按照这个思路我们便想到了构造最大生成树,将其余的边去除。

得到了这样一个树之后,我们便考虑如何求出两个节点之间最小边权的最大值(即为题中的最大载重),因为这两点之间的路径是唯一的,我们只需要找出这条路径便可以得到答案。我们可以通过LCA来做到这一点,我求LCA的方法是先从每一个根节点进行搜索,求出节点深度等信息,然后利用这些信息进行树上倍增。

于是我们可以得出大体思路:首先重新建图,构造出最大生成树,然后在最大生成树上求LCA来回答询问。

const int N=1e4+10,M=5e4+10;
int f[N][18],w[N][18],fa[N],deep[N];
int tt,n,m,q;

struct edge{
    int u,v,w;
    bool operator <(const edge &rhs)const{
        return w>rhs.w;
    }
}e[M<<1];

inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}

struct tree{//最大生成树
    int v,next,w;
}tree[N<<1];

int head[N],edge_num;
inline void adde(int u,int v,int w){
    tree[++edge_num].v=v;
    tree[edge_num].w=w;
    tree[edge_num].next=head[u];
    head[u]=edge_num;
}

inline void kruskal(){
    sort(e+1,e+m+1);
    rep(i,1,n)fa[i]=i;
    rep(i,1,m){
        int fx=find(e[i].u),fy=find(e[i].v);
        if(fx!=fy){
            fa[fx]=fy;
            adde(e[i].u,e[i].v,e[i].w);
            adde(e[i].v,e[i].u,e[i].w);
        }
    }
}

inline void dfs(int u,int dad,int val){
    w[u][0]=val;
    deep[u]=deep[dad]+1;
    f[u][0]=dad;
    rep(i,0,tt){
        f[u][i+1]=f[f[u][i]][i];
        w[u][i+1]=min(w[u][i],w[f[u][i]][i]);
    }
    for(int i=head[u];i;i=tree[i].next){
        int v=tree[i].v;
        if(v==dad)continue;
        dfs(v,u,tree[i].w);
    }
}

inline int lca(int u,int v){
    int res=INT_MAX;
    if(deep[u]<deep[v])swap(u,v);
    dwn(i,tt,0){
        if(deep[u]-(1<<i)>=deep[v]){
            res=min(res,w[u][i]);
            u=f[u][i];
        }
    }
    if(u==v)return res;
    dwn(i,tt,0){
        if(f[u][i]!=f[v][i]){
            res=min(res,min(w[u][i],w[v][i]));
            u=f[u][i],v=f[v][i];
        }
    }
    return min(res,min(w[u][0],w[v][0]));
}

int main(){
    mem(w,INT_MAX);
    rd(n),rd(m);
    tt=log2(n)+1;
    rep(i,1,m){
        rd(e[i].u),rd(e[i].v),rd(e[i].w);
    }
    kruskal();
    rep(i,1,n)//题上没有说是连通图,可能是森林
        if(i==fa[i])
            dfs(i,i,INT_MAX);
    rd(q);
    while(q--){
        int u,v;rd(u),rd(v);
        if(find(u)!=find(v))puts("-1");
        else printf("%d
",lca(u,v));
    }
    return 0;
}

以上是关于倍增专题的主要内容,如果未能解决你的问题,请参考以下文章

「kuangbin带你飞」专题十八 后缀数组

JSP编程专题1之JSP基础

《分布式微服务电商》专题-电商项目前端Vue模块化开发

树上倍增求LCA

代码源 Div1 - 105#451. Dis(倍增求LCA)

模板倍增