• 点分治


    点分治用来处理树上路径问题,每一次将树分治为几棵子树,然后继续递归,得到答案

    每次分治时,子树的根选取为其的重心,递归的子树大小不会超过原树大小的一半,保证了时间复杂度为(O(n log n))

    利用容斥原理统计答案

    树上有多少对点,满足两点间的距离小于等于(k)

    (code:)

    void dfs_root(int x,int fa)
    {
    	siz[x]=1,ma[x]=0;
    	for(int i=head[x];i;i=e[i].nxt)
    	{
    		int y=e[i].to;
    		if(vis[y]||y==fa) continue;
    		dfs_root(y,x);
    		siz[x]+=siz[y];
    		ma[x]=max(ma[x],siz[y]);
    	}
    	ma[x]=max(ma[x],tot-siz[x]);
    	if(ma[x]<ma[root]) root=x;
    }
    void dfs_dis(int x,int fa)
    {
    	len[++len_cnt]=dis[x];
    	for(int i=head[x];i;i=e[i].nxt)
    	{
    		int y=e[i].to,v=e[i].v;
    		if(vis[y]||y==fa) continue;
    		dis[y]=dis[x]+v;
    		dfs_dis(y,x);
    	}
    }
    int calc(int x,int lenth)
    {
        len_cnt=0,dis[x]=lenth;
        dfs_dis(x,0);
        sort(len+1,len+len_cnt+1);
        int sum=0,l=1,r=len_cnt;
        while(l<r)
    	{
    	    if(len[l]+len[r]<=k) sum+=r-l,l++;
    		else r--;
    	}
        return sum;
    }
    void solve(int x)
    {
        vis[x]=true,ans+=calc(x,0);
        for(int i=head[x];i;i=e[i].nxt)
        {
            int y=e[i].to,v=e[i].v;
            if(vis[y]) continue;
            ans-=calc(y,v);
            root=0,tot=siz[y];
            dfs_root(y,0);
            solve(root);
        }
    }
    
    ......
    
    tot=ma[0]=n;
    dfs_root(1,0);
    solve(root);
    

    动态点分治

    需要支持修改时,考虑用动态点分治解决

    通过构建一棵点分树,然后在点分树上修改,通过不断向上来跳父亲来维护信息

    点分树为点分治时每一层的重心之间连边的一棵树,高度为(log n)

    捉迷藏:给定一棵树,树上的点为黑色或白色,初始都为黑色,支持修改颜色和询问两个距离最远的黑点的距离

    维护三个堆:

    (q1):维护全局答案((q2)中最大值和次大值之和)

    (q2):维护一个节点到在点分树上所有儿子的子树内所有黑点的最大距离(节点所有儿子(q3)的堆顶)

    (q3):维护一个节点到在点分树中其子树内所有黑点到该节点父亲的距离

    (q2)的存在防止了最大值和次大值的合并时,两条链来自同一子树

    (code:)

    void dfs_root(int x,int fa)
    {
        size[x]=1,ma[x]=0,s[++cnt]=x;
        for(int i=head[x];i;i=e[i].nxt)
        {
            int y=e[i].to;
            if(vis[y]||y==fa) continue;
            dfs_root(y,x);
            size[x]+=size[y];
            ma[x]=max(ma[x],size[y]);
        }
        ma[x]=max(ma[x],tot-size[x]);
        if(ma[x]<ma[root]) root=x;
    }
    void solve(int x)
    {
        vis[x]=true;
        q2[x].push(0);
        for(int i=1;i<=cnt;++i)
            q3[x].push(dis(fa[x],s[i]));
        for(int i=head[x];i;i=e[i].nxt)
        {
            int y=e[i].to;
            if(vis[y]) continue;
            root=cnt=0,tot=size[y];
            dfs_root(y,x);
            fa[root]=x,y=root;
            solve(root);
            q2[x].push(q3[y].top());
        }
        q1.push(q2[x].len());
    }
    void modify(int x,int type)
    {
        q1.del(q2[x].len());
        if(type) q2[x].del(0);
        else q2[x].push(0);
        q1.push(q2[x].len());
        for(int i=x;fa[i];i=fa[i])
        {
            int l1=q3[i].top(),l2,dist=dis(fa[i],x);
            if(type) q3[i].del(dist);
            else q3[i].push(dist);
            l2=q3[i].top();
            q1.del(q2[fa[i]].len());
            if(l1!=-inf) q2[fa[i]].del(l1);
            if(l2!=-inf) q2[fa[i]].push(l2);
            q1.push(q2[fa[i]].len());
        }
    }
    

    点分树树高为 (log n),所有点的子树和为 (n log n)

  • 相关阅读:
    C++指针笔记
    破解入门【OllyDebug爆破程序】
    c++类的定义《一》
    数组
    while循环语句的使用
    MS10-046漏洞测试
    For循环语句的使用
    C++Builder编写计算器
    C++自定义函数
    SQLyog简介
  • 原文地址:https://www.cnblogs.com/lhm-/p/12229531.html
Copyright © 2020-2023  润新知