zoukankan      html  css  js  c++  java
  • [模板] 树的重心/点分治/动态点分治

    树的重心

    树的其他问题 - OI Wiki

    定义

    ((1)) 树的重心 (leftrightarrow) 所有的子树中最大的子树节点数最少的节点.

    ((2)) 树的重心 (leftrightarrow) 所有子树的大小都不超过整个树大小的一半的节点.

    性质

    删去重心后, 生成的多棵树尽可能平衡.

    一棵树中最少有两个重心.

    树中所有点到某个点的距离和中, 到重心的距离和是最小的; 如果有两个重心, 那么他们的距离和一样.

    把两个树通过一条边相连得到一个新的树, 那么新的树的重心在连接原来两个树的重心的路径上.

    把一个树添加或删除一个叶子, 那么它的重心最多只移动一条边的距离.

    求法&&代码

    根据定义((1)), dfs求即可.

    int szp[nsz],sum,maxp[nsz]{2e4+5},rt=0;
    
    void getrt(int p,int fa){
    	szp[p]=1,maxp[p]=0;
    	forg(p,i,v){
    		if(v==fa)continue;
    		getrt(v,p);
    		szp[p]+=szp[v];
    		maxp[p]=max(maxp[p],szp[v]);
    	}
    	maxp[p]=max(maxp[p],sum-szp[p]);
    	if(maxp[p]<maxp[rt])rt=p;
    }
    

    点分治

    简介

    点分治可以解决一些树上的路径问题.

    对于一棵无根树, 可以将一个点 (rt) 设为根, 把它转化为有根树. 那么树上的路径可以分为两类:

    1. 经过根的路径;
    2. 仅在某个子树内的路径.

    对于1, 我们可以将链 ((a,b)) 分为 ((a,rt)), ((rt,b)), 多数情况可以合并他们的影响 (例如求路径长度). 一般有两种做法:

    • 处理 (rt) 及其所有子节点, 求出两两之间的对答案的贡献, 再减去同一子树内的点对对答案的贡献.

    • 分别递归 (rt) 的每个子树, 对每个子树求出其与之前的子树中的节点形成点对的贡献. 这样就不需要容斥减去重复的部分.

    对于2, 可以递归求解.

    可以证明, 如果在递归每个子树时都将子树的重心设为根, 那么递归层数是 (O(log n)) 的.

    代码

    //store the tree
    const int nsz=20050;
    
    ll n;
    struct te{int t,pr,v;}edge[nsz*2];
    int hd[nsz],pe=1;
    void adde(int f,int t,int v){edge[++pe]=(te){t,hd[f],v};hd[f]=pe;}
    void adddb(int f,int t,int v){adde(f,t,v);adde(t,f,v);}
    #define forg(p,i,v) for(int i=hd[p],v=edge[i].t;i;i=edge[i].pr,v=edge[i].t)
    
    //find center of tree
    int vi[nsz];
    int szp[nsz],sum,maxp[nsz]{2e4+5},rt;
    
    void getrt(int p,int fa){
    	szp[p]=1,maxp[p]=0;
    	forg(p,i,v){
    		if(vi[v]||v==fa)continue;
    		getrt(v,p);
    		szp[p]+=szp[v];
    		maxp[p]=max(maxp[p],szp[v]);
    	}
    	maxp[p]=max(maxp[p],sum-szp[p]);
    	if(maxp[p]<maxp[rt])rt=p;
    }
    
    void sol1(int rt,int v0,int fl){
    //do something
    }
    
    void divide(int p){
    	int sum0=sum;
    	vi[p]=1;
    	sol1(p,0,1);
    	forg(p,i,v){
    		if(vi[v])continue;
    		sol1(v,edge[i].v,-1);
    		rt=0,sum=sum0-maxp[v],getrt(v,0);
    		divide(rt);
    	}
    }
    
    ll sol(){
    	rt=0,sum=n,getrt(1,0); //init
    	divide(rt);
    	return ans;
    }
    

    例题

    bzoj2152-[国家集训队]聪聪可可

    动态点分治

  • 相关阅读:
    完美配置Tomcat的HTTPS
    分享一个免费SSL证书申请网站,给网站开启https协议 | 张戈博客
    使用HttpClient发送HTTPS请求以及配置Tomcat支持SSL
    HttpClient工具类v1.7
    列出某文件夹及其子文件夹下面的文件,并可根据扩展名过滤
    拷贝一个目录或者文件到指定路径下
    判断字符是否属于中文
    Windows 7更改SVN账户密码
    FileObverse文件观察者的Debug报告
    jquery.cookie.js操作cookie实现“记住密码”,很简单很强大
  • 原文地址:https://www.cnblogs.com/ubospica/p/10408644.html
Copyright © 2011-2022 走看看