• 点分治学习


    点分治

    嗯,蒟蒻我刚学的就记录一下
    以洛谷的tree为模板讲解:洛谷题目传送门

    树的重心

    了解点分治之前,首先要知道什么是重心(要用到)
    简单来说,就是子树最小的那个节点,我们需要O(n)地找到他来保证复杂度

    void get_root(rg int now,rg int fm)
    {
    	size[now]=1;rg int num=0;//size[]记录子树大小,num[]为当前的最大子树
    	for(rg int i=hd[now];i;i=ljl[i].nxt)//遍历
    	{
    		rg int qw=ljl[i].to;
    		if(qw==fm||vis[qw])continue;//先不管这个vis[]
    		get_root(qw,now);
    		size[now]+=size[qw];
    		num=max(num,size[qw]);
    	}
    	num=max(num,tot-size[now]);//tot是总点数
    	                           //tot-size[now]是父亲那边(不是儿子)的“子树”大小
    	if(Max>num)Max=num,root=now;//是否更新
    }
    

    个人认为这里还是比较简单的,自己画个图简单明了了

    点分树

    找到重心后把重心删掉,变成两个连通块,再分别找重心,把重心与之前找的重心相连边,不断递归建成一棵新树,就是点分树
    &&优点:严格log层(不信自己验证)
    这道题暂时不要用,但是是点分治的一个重要知识点……

    点分治

    首先了解几个特点(暂时不用理解,等下自然就懂)

    • 边建边算
    • 常见问题:
      1.是否存在……的路径
      2.满足……的路径有几条
    • 只要点分治之后的处理是线性的,那么总的复杂度一定是nlogn(只要你不瞎搞)

    嗯,不想写了,所以安利一波机房大佬的博客理解吧,一时半会真的讲不清,至少我问了很久才懂(主要是不想画图来讲解了,画图就很容易理解了)
    YCB blog
    我也推荐几道题吧,毕竟没总结良心过不去(其实就是给自己日后复习用的)

    code还是不少

    #include<iostream>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<iomanip>
    #include<algorithm>
    #include<ctime>
    #include<queue>
    #include<stack>
    #include<vector>
    #define rg register
    #define il inline
    #define lst long long
    #define ldb long double
    #define N 40050
    using namespace std;
    const int Inf=1e9;
    
    int n,K,cnt,tot,ans;
    int Max,root,le,ri;
    struct EDGE{
        int to,nxt,v;
    }ljl[N<<1];
    int hd[N];
    int size[N],vis[N];
    int Q[N],dis[N];
    
    il int read()
    {
        rg int s=0,m=0;rg char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')m=1;ch=getchar();}
        while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
        return m?-s:s;
    }
    
    il void add(rg int p,rg int q,rg int o)
    {
        ljl[++cnt]=(EDGE){q,hd[p],o};hd[p]=cnt;
    }
    
    void get_root(rg int now,rg int fm)
    {
        size[now]=1;rg int num=0;
        for(rg int i=hd[now];i;i=ljl[i].nxt)
        {
            rg int qw=ljl[i].to;
            if(qw==fm||vis[qw])continue;
            get_root(qw,now);
            size[now]+=size[qw];
            num=max(num,size[qw]);
        }
        num=max(num,tot-size[now]);
        if(Max>num)Max=num,root=now;
    }
    
    void get_dis(rg int now,rg int fm)
    {
        Q[++ri]=dis[now];
        for(rg int i=hd[now];i;i=ljl[i].nxt)
        {
            rg int qw=ljl[i].to;
            if(vis[qw]||qw==fm)continue;
            dis[qw]=dis[now]+ljl[i].v;
            get_dis(qw,now);
        }
    }
    
    il int Query(rg int now,rg int base)
    {
        rg int res=0;
        ri=0;dis[now]=base;
        le=1;get_dis(now,0);
        sort(Q+1,Q+ri+1);
        while(le<=ri)
        {
            if(Q[le]+Q[ri]<=K)res+=ri-le,le++;
            else ri--;
        }
        return res;
    }
    
    void divide(rg int now,rg int fm)
    {
        ans+=Query(now,0);vis[now]=1;
        rg int all=tot;
        for(rg int i=hd[now];i;i=ljl[i].nxt)
        {
            rg int qw=ljl[i].to;
            if(qw==fm||vis[qw])continue;
            ans-=Query(qw,ljl[i].v);
            tot=size[now]>size[qw]?size[qw]:all-size[qw];Max=Inf;
            get_root(qw,0),divide(qw,0);
        }
    }
    
    int main()
    {
        n=read();
        for(rg int i=1;i<n;++i)
        {
            rg int p=read(),q=read(),o=read();
            add(p,q,o),add(q,p,o);
        }
        K=read();
        tot=n,Max=Inf;
        get_root(1,0);
        divide(root,0);
        printf("%d
    ",ans);
        return 0;
    }
    
    

    放几道题目(我也放了题解在blog)

    tree(就在上面)
    [国家集训队]聪聪可可(题解)
    【模板】点分治1(题解)

  • 相关阅读:
    ecstore前台模板变量处理
    MySQL慢查询日志总结
    Linux 操作MySQL常用命令行
    CGI、FastCGI和php-fpm的概念和区别
    sync实现windows与nginx主机端文件同步(参考文档)
    PHP实现四种基本排序算法
    http和https协议
    linux的计划任务
    php封装一个接口类
    mysql的常见面试问题
  • 原文地址:https://www.cnblogs.com/cjoierljl/p/9343592.html
Copyright © 2020-2023  润新知