zoukankan      html  css  js  c++  java
  • 点分治总结

    点分治

    点分治就是基于点的分治。
    方法如下:dfs遍历整棵树,在遍历到点u时,考虑LCA为u的点对组成的路径,方法如下:
    依次考虑u的所有子节点v,对这些v的子树进行遍历,并考虑每个点与之前遍历的点组成的路径。
    这种方法的时间复杂度为(O(子树大小之和))
    但这样对一些极限数据会超时(例如一条链的情况),可能退化到(O(n^2))
    考虑优化:
    因为每次考虑点对,都是在以u为根的子树中考虑,与其它节点无关(其他点对的LCA不可能是u)。
    所以每次dfs到u时,在处理完u为根的点对后,接下来要考虑u的子节点v,v的子树就相当于是一棵独立的树,所以对于这棵独立的树的dfs可以从任意节点开始继续搜索。为了让效率尽量高,我们应当选取树的重心作为根。
    可以证明,这样的最坏时间复杂度为(O(nlogn))

    树的重心也叫树的质心。对于一棵树n个节点的无根树,找到一个点,使得把树变成以该点为根的有根树时,最大子树的结点数最小。换句话说,删除这个点后最大连通块(一定是树)的结点数最小。

    如图:

    找重心可以一遍dfs得出。

    例题1

    洛谷P4149 [IOI2011]Race
    求树中长度为K,且经过边数最少的路径。((K leq 10^6))

    思路:
    开一个数组,记录长度为(i)的路径的最少边数,遍历到长度为(i)的路径时,查询长度为(K-i)的路径。
    清数组不能暴力,要开一个栈,记录所有修改,再针对这些修改清空。

    代码

    #include <stdio.h>
    int fr[200010],ne[400010];
    int v[400010],w[400010],bs=0;
    void addb(int a,int b,int c)
    {
        v[bs]=b;
        w[bs]=c;
        ne[bs]=fr[a];
        fr[a]=bs;
        bs+=1;
    }
    int mi,wz,dx[200010];
    bool bk[200010]={0};
    void getroot(int u,int f,int sl)
    {
        dx[u]=1;
        int ma=-1;
        for(int i=fr[u];i!=-1;i=ne[i])
        {
            if(v[i]==f||bk[v[i]])
                continue;
            getroot(v[i],u,sl);
            if(dx[v[i]]>ma)
                ma=dx[v[i]];
            dx[u]+=dx[v[i]];
        }
        if(sl-dx[u]>ma)
            ma=sl-dx[u];
        if(ma<mi)
        {
            mi=ma;
            wz=u;
        }
    }
    int zx[1000010],xg[200010],cd[200010],gs,K,jg,inf=99999999;
    void dfs2(int u,int f,int jl,int b)
    {
        if(jl<=K)
        {
            xg[gs]=jl;
            cd[gs++]=b;
            if(zx[K-jl]+b<jg)
                jg=zx[K-jl]+b;
        }
        else
            return;
        for(int i=fr[u];i!=-1;i=ne[i])
        {
            if(v[i]==f||bk[v[i]])
                continue;
            dfs2(v[i],u,jl+w[i],b+1);
        }
    }
    void solve(int u)
    {
        gs=0;
        for(int i=fr[u];i!=-1;i=ne[i])
        {
            if(bk[v[i]])
                continue;
            int la=gs;
            dfs2(v[i],u,w[i],1);
            for(int i=la;i<gs;i++)
            {
                if(cd[i]<zx[xg[i]])
                    zx[xg[i]]=cd[i];
            }
        }
        for(int i=0;i<gs;i++)
        {
            if(xg[i]==K&&zx[xg[i]]<jg)
                jg=zx[xg[i]];
            zx[xg[i]]=inf;
        }
    }
    int dfs3(int u,int f)
    {
        int rtn=1;
        for(int i=fr[u];i!=-1;i=ne[i])
        {
            if(v[i]!=f&&!bk[v[i]])
                rtn+=dfs3(v[i],u);
        }
        return rtn;
    }
    void dfs1(int u)
    {
        bk[u]=true;
        solve(u);
        for(int i=fr[u];i!=-1;i=ne[i])
        {
            if(bk[v[i]])
                continue;
            mi=inf;
            getroot(v[i],u,dfs3(v[i],u));
            dfs1(wz);
        }
    }
    int main()
    {
        int n;
        scanf("%d%d",&n,&K);
        for(int i=0;i<n;i++)
            fr[i]=-1;
        for(int i=0;i<n-1;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            addb(a,b,c);
            addb(b,a,c);
        }
        jg=mi=inf;
        for(int i=0;i<=1000000;i++)
            zx[i]=inf;
        getroot(0,-1,n);
        dfs1(wz);
        if(jg==inf)
            printf("-1");
        else
            printf("%d",jg);
        return 0;
    }
    

    例题2

    [BJOI2017]树的难题
    将每个点的子树按颜色排序,并使用线段树维护最大权值。
    时间复杂度(O(nlog ^2n))
    (nleq2*10^5),时限2秒,会卡常。
    考虑如下常数优化:
    由于边权为1,所以计算每棵子树时,最大的长度就是这个节点到叶子结点的最长链长度(设为m)。
    在计算时,将线段树重建,范围为0~m。
    这样,查询时的复杂度就会小一些,变成(O(log m))
    并且无需在最后清空线段树,直接重建时清空就行了。
    这样就能过了。

    代码:

    #include <stdio.h>
    #include <stdlib.h>
    int fr[200010],ne[400010],n;
    int v[400010],w[400010],bs=0;
    int C[200010];
    int inf=999999999;
    #define re register
    inline int max(int a,int b)
    {
    	return a>b?a:b;
    }
    void addb(int a,int b,int c)
    {
        v[bs]=b;
        w[bs]=c;
        ne[bs]=fr[a];
        fr[a]=bs;
        bs+=1;
    }
    struct SXds
    {
        int zd[800010],sz[200010];
        void jianshu(re int i,re int l,re int r)
        {
            zd[i]=-inf;
            if(l+1==r)
            {
                sz[l]=-inf;
                return;
            }
            int m=(l+r)>>1;
            jianshu(i<<1,l,m);
            jianshu((i<<1)|1,m,r);
        }
        void pushup(int i)
        {
            zd[i]=zd[(i<<1)|1];
            if(zd[i<<1]>zd[i])
                zd[i]=zd[i<<1];
        }
        void xiugai(re int i,re int l,re int r,re int j,re int x)
        {
            if(l+1==r)
            {
                zd[i]=sz[j]=x;
                return;
            }
            re int m=(l+r)>>1;
            if(j<m)
                xiugai(i<<1,l,m,j,x);
            else
                xiugai((i<<1)|1,m,r,j,x);
            pushup(i);
        }
        int chaxun(re int i,re int l,re int r,re int L,re int R)
        {
            if(r<=L||R<=l)
                return -inf;
            if(L<=l&&r<=R)
                return zd[i];
            re int m=(l+r)>>1;
            re int t1=chaxun(i<<1,l,m,L,R),t2=chaxun((i<<1)|1,m,r,L,R);
            if(t2>t1)
                t1=t2;
            return t1;
        }
    };
    SXds xt,bt;
    bool bk[200010];
    int dfs1(re int u,re int f)
    {
        int rt=1;
        for(re int i=fr[u];i!=-1;i=ne[i])
        {
            if(v[i]!=f&&!bk[v[i]])
                rt+=dfs1(v[i],u);
        }
        return rt;
    }
    int mi=inf,wz;
    int dfs2(re int u,re int f,re int si)
    {
        int zd=-1,rt=1;
        for(re int i=fr[u];i!=-1;i=ne[i])
        {
            if(v[i]!=f&&!bk[v[i]])
            {
                int t=dfs2(v[i],u,si);
                rt+=t;
                if(t>zd)
                    zd=t;
            }
        }
        if(si-rt>zd)
            zd=si-rt;
        if(zd<mi)
        {
            mi=zd;
            wz=u;
        }
        return rt;
    }
    int st[200010],qz[200010],cd[200010],tp=0,jg=-inf,cl,cr;
    struct SPx
    {
        int z,u;
    };
    SPx px[200010];
    int cmp(const void*a,const void*b)
    {
        return ((SPx*)a)->z-((SPx*)b)->z;
    }
    int zc;
    void dfs5(int u,int f,int sd)
    {
    	if(sd>zc)
    		zc=sd;
    	for(int i=fr[u];i!=-1;i=ne[i])
    	{
    		if(v[i]!=f&&!bk[v[i]])
    			dfs5(v[i],u,sd+1);
    	}
    }
    void dfs3(re int u,re int f,re int co,re int la,re int he,re int jl)
    {
        qz[u]=he+C[la];
        cd[u]=jl;
        st[tp++]=u;
        re int t=xt.chaxun(1,0,zc,cl-jl,cr-jl+1)+he-C[co]+C[la];
        if(t>jg)
            jg=t;
        t=bt.chaxun(1,0,zc,cl-jl,cr-jl+1)+he+C[la];
        if(t>jg)
            jg=t;
        for(re int i=fr[u];i!=-1;i=ne[i])
        {
            if(v[i]!=f&&!bk[v[i]])
                dfs3(v[i],u,co,w[i],(w[i]==la?he:he+C[la]),jl+1);
        }
    }
    void solve(int u)
    {
        re int sl=0;
        for(re int i=fr[u];i!=-1;i=ne[i])
        {
            if(!bk[v[i]])
            {
                px[sl].u=v[i];
                px[sl++].z=w[i];
            }
        }
        qsort(px,sl,sizeof(SPx),cmp);
        tp=0;
        re int la,lx=0;
    	zc=-inf;
    	dfs5(u,-1,0);
    	zc+=1;
    	xt.jianshu(1,0,zc);
    	bt.jianshu(1,0,zc);
    	bt.xiugai(1,0,zc,0,0);
        for(re int i=0;i<sl;i++)
        {
            la=tp;
            if(i>0&&px[i].z!=px[i-1].z)
            {
                for(re int j=lx;j<tp;j++)
                {
                    xt.xiugai(1,0,zc,cd[st[j]],-inf);
                    if(qz[st[j]]>bt.sz[cd[st[j]]])
                        bt.xiugai(1,0,zc,cd[st[j]],qz[st[j]]);
                }
                lx=tp;
            }
            dfs3(px[i].u,u,px[i].z,px[i].z,0,1);
            for(int j=la;j<tp;j++)
            {
                if(qz[st[j]]>xt.sz[cd[st[j]]])
                    xt.xiugai(1,0,zc,cd[st[j]],qz[st[j]]);
            }
        }
    }
    void dfs4(int u)
    {
        bk[u]=true;
        solve(u);
        for(int i=fr[u];i!=-1;i=ne[i])
        {
            if(!bk[v[i]])
            {
                mi=inf;
                int si=dfs1(v[i],u);
                dfs2(v[i],u,si);
                dfs4(wz);
            }
        }
    }
    int main()
    {
        int m;
        scanf("%d%d%d%d",&n,&m,&cl,&cr);
        for(int i=1;i<=n;i++)
            fr[i]=-1;
        for(int i=1;i<=m;i++)
            scanf("%d",&C[i]);
        for(int i=0;i<n-1;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            addb(a,b,c);
            addb(b,a,c);
        }
        mi=inf;
        dfs2(1,-1,n);
        dfs4(wz);
        printf("%d",jg);
        return 0;
    }
    
    
  • 相关阅读:
    调用Win32 API netapi32.dll 实现UNC(网络共享)连接的管理(一)
    一个重写Page基类的例子
    36进制进位算法例子
    关于.net 中调用script的alert后 css失效的办法
    Javascript:keyCode键码值表
    Url地址重写,利用HttpHander手工编译页面并按需生成静态HTML文件
    在.NET程序中正确使用String类型
    Sql Server中自动序号的方法
    托管和非托管的关系和区别
    Oracle 格式化
  • 原文地址:https://www.cnblogs.com/lnzwz/p/11246665.html
Copyright © 2011-2022 走看看