• HIT暑期集训 lca与rmq


     rmq,求区间最大最小值模板,以POJ - 3264为例

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define maxn 50005
    using namespace std;
    int a[maxn],dp[maxn][30][2];
    void ST(int n) 
    {
        int i,j;
        for (i=1;i<=n;i++) dp[i][0][0]=dp[i][0][1]=a[i];
        for (j=1;(1<<j)<=n;j++) 
        {
            for (i=1;i+(1<<j)-1<=n;i++) 
            {
                dp[i][j][0]=max(dp[i][j-1][0],dp[i+(1<<(j-1))][j-1][0]);
                dp[i][j][1]=min(dp[i][j-1][1],dp[i+(1<<(j-1))][j-1][1]);
            }
        }
    }
    int getk(int l,int r)
    {
        int k=0;
        while ((1<<(k+1))<=r-l+1) k++;
        return k;
    }
    int rmq_max(int l,int r,int k) 
    {
        return max(dp[l][k][0],dp[r-(1<<k)+1][k][0]);
    }
    int rmq_min(int l,int r,int k) 
    {
        return min(dp[l][k][1],dp[r-(1<<k)+1][k][1]);
    }
    int main()
    {
        int i,x,y,k,n,q,ans;
        scanf("%d%d",&n,&q);
        for (i=1;i<=n;i++) scanf("%d",&a[i]);
        ST(n);
        while (q--) 
        {
            scanf("%d%d",&x,&y);
            k=getk(x,y);
            ans=rmq_max(x,y,k)-rmq_min(x,y,k);
            printf("%d
    ",ans);
        }
        return 0;
    }
    rmq模板

    树链剖分求lca模板,以POJ - 1330为例

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define maxn 10005
    using namespace std;
    struct edge
    {
        int to,nxt;
    }e[maxn];
    int num,last[maxn],deg[maxn];
    int fa[maxn],dep[maxn],siz[maxn],son[maxn],top[maxn];
    void add(int x,int y)
    {
        e[++num].to=y;
        e[num].nxt=last[x];
        last[x]=num;
    }
    void dfs1(int u,int father,int depth)  
    {
        int i,v;
        fa[u]=father;
        dep[u]=depth;
        siz[u]=1;
        for (i=last[u];i;i=e[i].nxt)
        {
            v=e[i].to;
            if (v==father) continue;
            dfs1(v,u,depth+1);    
            siz[u]+=siz[v];    
            if (siz[v]>siz[son[u]]) son[u]=v;   
        }
    }
    void dfs2(int u,int tp)   
    {
        top[u]=tp; 
        if (!son[u]) return;
        dfs2(son[u],tp);
        int i,v;
        for (i=last[u];i;i=e[i].nxt)
        {
            v=e[i].to;
            if (v!=son[u] && v!=fa[u]) dfs2(v,v);  
        }
    }
    int query(int x,int y)
    {
        int tx=top[x],ty=top[y];
        while (tx!=ty)    
        {
            if (dep[tx]>=dep[ty]) x=fa[tx],tx=top[x];
            else y=fa[ty],ty=top[y];
        }
        if (dep[x]>dep[y]) return y;
        return x;
    }
    int main()
    {
        int T,i,x,y,n,rt;
        scanf("%d",&T);
        while (T--)
        {
            num=0;
            memset(last,0,sizeof(last));
            memset(son,0,sizeof(son));
            memset(deg,0,sizeof(deg));
            scanf("%d",&n);
            for (i=1;i<n;i++)
            {
                scanf("%d%d",&x,&y);
                add(x,y);deg[y]++;
            }
            for (i=1;i<=n;i++)
                if (deg[i]==0) 
                {
                    rt=i;break;
                }
            dfs1(rt,0,0);
            dfs2(rt,0);
            scanf("%d%d",&x,&y);
            printf("%d
    ",query(x,y));
        }
        return 0;
    }
    树链剖分求lca

    D    HYSBZ 1047

    E    CodeForces 803G

    题意:给出一个长度为n的区间,将它复制k倍形成一个长度为n*k的区间。接下来有q个操作,操作1将区间(l,r)中的所有数改为x,操作2询问区间(l,r)中的最小值。

    其中n<=1e5,k<=1e4,q<=1e5

    思路:动态开点线段树+rmq。

    n*k<=1e9,显然不能直接使用rmq或者线段树求解。

    发现q<=1e5,若动态开点线段树(就是每次操作到一个新的区间再开新的点),q次操作最多增加2*q*log(n*k)(<=6e6)个节点。

    于是用rmq维护最开始的n长区间内的最小值,当开点时根据rmq为线段树节点赋值。

    #include<cstdio> 
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int maxn=100010;
    const int maxnode=6000010;
    const int inf=1e9+7; 
    int tot,n,a[maxn],dp[maxn][30],prelog2[maxn];
    struct node
    {
        int val,tag;
        int ch[2];
    }tr[maxnode];
    void prework(int n)//预处理,求对数 
    {
        prelog2[0]=-1;
        for (int i=1;i<=n;i++) 
            if (((i-1)&i)==0) prelog2[i]=prelog2[i-1]+1;//此时i为2^k 
            else prelog2[i]=prelog2[i-1];
    }
    void ST(int n)//初始数组的区间最小值 
    {
        int i,j;
        for (i=1;i<=n;i++) dp[i][0]=a[i];
        for (j=1;(1<<j)<=n;j++) 
            for (i=1;i+(1<<j)-1<=n;i++) 
                dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
    }
    int st_query(int l,int r) 
    {
        int k=prelog2[r-l+1];
        return min(dp[l][k],dp[r-(1<<k)+1][k]);
    }
    void pushup(int k)
    {
        tr[k].val=min(tr[tr[k].ch[0]].val,tr[tr[k].ch[1]].val);
    }
    void pushdown(int k)
    {
        tr[tr[k].ch[0]].val=tr[k].tag;
        tr[tr[k].ch[0]].tag=tr[k].tag;
        tr[tr[k].ch[1]].val=tr[k].tag;
        tr[tr[k].ch[1]].tag=tr[k].tag;
        tr[k].tag=0;
    }
    void newnode(int k,int t,int l,int r) 
    {
        tr[k].ch[t]=++tot;//新节点编号 
        //新节点所代表的区间没有被修改过 
        if (r-l+1>=n) tr[tot].val=st_query(1,n);
        else 
        {
            int ll=l%n,rr=r%n;
            if (!ll) ll=n;
            if (!rr) rr=n;
            if (rr<ll) tr[tot].val=min(st_query(1,rr),st_query(ll,n));
            else tr[tot].val=st_query(ll,rr);
        }
    }
    void update(int L,int R,int l,int r,int k,int x)
    {
        if (L<=l && r<=R)
        {
            tr[k].val=x;
            tr[k].tag=x;
            return;
        }
        int mid=(l+r)>>1;
        if (!tr[k].ch[0]) newnode(k,0,l,mid);
        if (!tr[k].ch[1]) newnode(k,1,mid+1,r);
        if (tr[k].tag) pushdown(k);
        if (L<=mid) update(L,R,l,mid,tr[k].ch[0],x);
        if (R>mid) update(L,R,mid+1,r,tr[k].ch[1],x);
        pushup(k);
    }
    int query(int ql,int qr,int l,int r,int k)
    {
        if (ql<=l && r<=qr) return tr[k].val;
        int mid=(l+r)>>1;
        if (!tr[k].ch[0]) newnode(k,0,l,mid);
        if (!tr[k].ch[1]) newnode(k,1,mid+1,r);
        int re=inf;
        if (tr[k].tag) pushdown(k);
        if (ql<=mid) re=min(re,query(ql,qr,l,mid,tr[k].ch[0]));
        if (qr>mid) re=min(re,query(ql,qr,mid+1,r,tr[k].ch[1]));
        pushup(k);
        return re;
    }
    int main()
    {
        int op,l,r,x;
        int i,k,q;
        scanf("%d%d",&n,&k);
        for (i=1;i<=n;i++) scanf("%d",&a[i]);
        prework(n);
        ST(n);
        tr[1].val=st_query(1,n);
        tot=1;
        scanf("%d",&q);
        while (q--)
        {
            scanf("%d",&op);
            if (op==1)
            {
                scanf("%d%d%d",&l,&r,&x);
                update(l,r,1,n*k,1,x);
            }
            else 
            {
                scanf("%d%d",&l,&r);
                printf("%d
    ",query(l,r,1,n*k,1));
            }
        }
        return 0;
    }
    View Code

    F    CodeForces 832D

    题意:给一棵树,每次询问给三个点,这三个点可以随意排列,分别为s,f,t,问s到f的路径上与t到f的路径上有几个两路径共有的节点。

    可以发现答案为(dis[s->f]+dis[t->f]-dis[s->t])/2+1(由于是求共有节点所以要+1),而dis[x->y]=dep[x]+dep[y]-2*dep[lca(x,y)],

    用树链剖分就可求出dep与lca,从而求出dis。

    对于每次询问,枚举三种f的情况,其中的最大值就是答案。

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define maxn 100005
    using namespace std;
    struct edge
    {
        int to,nxt;
    }e[maxn<<1];
    int num,last[maxn];
    int fa[maxn],dep[maxn],siz[maxn],son[maxn],top[maxn];
    void add(int x,int y)
    {
        e[++num].to=y;
        e[num].nxt=last[x];
        last[x]=num;
    }
    void dfs1(int u,int father,int depth)  
    {
        int i,v;
        fa[u]=father;
        dep[u]=depth;
        siz[u]=1;
        for (i=last[u];i;i=e[i].nxt)
        {
            v=e[i].to;
            if (v==father) continue;
            dfs1(v,u,depth+1);    
            siz[u]+=siz[v];    
            if (siz[v]>siz[son[u]]) son[u]=v;   
        }
    }
    void dfs2(int u,int tp)   
    {
        top[u]=tp;
        if (!son[u]) return;
        dfs2(son[u],tp);
        int i,v;
        for (i=last[u];i;i=e[i].nxt)
        {
            v=e[i].to;
            if (v!=son[u] && v!=fa[u]) dfs2(v,v);  
        }
    }
    int lca(int x,int y)
    {
        int tx=top[x],ty=top[y];
        while (tx!=ty)    
        {
            if (dep[tx]>=dep[ty]) x=fa[tx],tx=top[x];
            else y=fa[ty],ty=top[y];
        }
        if (dep[x]>dep[y]) return y;
        return x;
    }
    int getdis(int x,int y)
    {
        int xy=lca(x,y);
        return dep[x]+dep[y]-2*dep[xy];
    } 
    int getans(int s,int f,int t)
    {
        return (getdis(s,f)+getdis(f,t)-getdis(s,t))/2;
    } 
    int main()
    {
        int T,i,x,y,z,n,q,ans;
        while (scanf("%d%d",&n,&q)!=EOF)
        {
            num=0;
            memset(last,0,sizeof(last));
            memset(son,0,sizeof(son));
            for (i=2;i<=n;i++)
            {
                scanf("%d",&x);
                add(x,i);add(i,x);
            }
            dfs1(1,0,0);
            dfs2(1,0);
            while (q--)
            {
                scanf("%d%d%d",&x,&y,&z);
                ans=getans(x,y,z);
                ans=max(ans,getans(y,x,z));
                ans=max(ans,getans(x,z,y));
                printf("%d
    ",ans+1);
            }
        }
        return 0;
    }
    View Code

    G    HDU 5023

    H    HYSBZ 4810

    J    POJ 2019

    题意:给定一个n*n矩阵与整数b,每次询问输入x,y,求以(x,y)为左上顶点的b*b矩阵中的最大值与最小值的差。

    二维rmq。对于每行做rmq,即上文rmq模板中的dp[i][j][0/1]加上一维改为dp[k][i][j][0/1],

    其中k代表行数,i为区间左端点,i+2j为区间右端点,第四维0代表区间最大值,1代表区间最小值。

    然后对于每次询问for一遍行数统计答案就完事了。

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define maxn 255
    using namespace std;
    int n,b,a[maxn][maxn];
    int dp[maxn][maxn][30][2];
    //第一维代表rmq维护的行数。第四维0代表区间最大值,1代表区间最小值。 
    void ST() 
    {
        int i,j,k;
        for (k=1;k<=n;k++)
        for (i=1;i<=n;i++) dp[k][i][0][0]=dp[k][i][0][1]=a[k][i];
        for (k=1;k<=n;k++)
        for (j=1;(1<<j)<=n;j++) 
        {
            for (i=1;i+(1<<j)-1<=n;i++) 
            {
                dp[k][i][j][0]=max(dp[k][i][j-1][0],dp[k][i+(1<<(j-1))][j-1][0]);
                dp[k][i][j][1]=min(dp[k][i][j-1][1],dp[k][i+(1<<(j-1))][j-1][1]);
            }
        }
    }
    int main()
    {
        int i,j,x,y,k,b,q,ans[2];
        while (scanf("%d%d%d",&n,&b,&q)!=EOF)
        {
            for (i=1;i<=n;i++) 
            for (j=1;j<=n;j++) scanf("%d",&a[i][j]);
            ST();
            k=0;
            while ((1<<(k+1))<=b) k++;
            while (q--) 
            {
                ans[0]=-1;ans[1]=251;
                scanf("%d%d",&x,&y);
                for (i=x;i<x+b;i++)
                {
                    ans[0]=max(ans[0],dp[i][y][k][0]);
                    ans[0]=max(ans[0],dp[i][y+b-(1<<k)][k][0]);
                    ans[1]=min(ans[1],dp[i][y][k][1]);
                    ans[1]=min(ans[1],dp[i][y+b-(1<<k)][k][1]);
                }
                printf("%d
    ",ans[0]-ans[1]);
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Mirco2440核心板设计思考
    linux 第一次获得root权限
    MakeFile 文件详解
    windows下编辑过的文件在Linux下用vi打开行尾会多出一个^M符号
    linux信息查找
    ubuntu不能正常使用make menuconfig的解决方案
    Linux 解压/压缩操作命令
    Linux 文件/文件夹操作命令
    Linux内核开发基础
    计算文件夹的大小
  • 原文地址:https://www.cnblogs.com/lsykk/p/13538395.html
Copyright © 2020-2023  润新知