首页 > 其他分享 >点分治板题

点分治板题

时间:2023-08-24 16:55:46浏览次数:38  
标签:lg 子树 板题 int 分治 mx define

点分治

点分治的思想:每次选定一个点,然后分别在它的子树内求解①,再计算跨越子树的情况,考虑当前点和每棵子树内的点②以及两两子树内的③。(有点绕)

image-20230824163215440

如图,红代表①,紫代表②,绿代表③。

这样求解的复杂度取决去递归的层数,若树是一条链,那么若每次选择的是链的一个端点,那么一共会有 \(n\) 层,将会超时。

实际上只要选择重心进行分治,就能稳定 \(O(\lg n)\) 层。

为什么呢,因为重心有一个性质:对于重心而言,它任意子节点的子树大小一定不超过自己子树大小 \(n\) 的 \(\dfrac{1}{2}\)。

这可以使用反证法来证明,假设存在这么一个点,那么这个点的子树大小 \(>n\div 2\),则除这部分点外的大小 \(<n\div 2<k\),而这个点的内部就算只有一棵子树,它的子树大小一定 \(\le k-1\)(因为重心要去除自己这个点之后计算),二者均小于最大子树,则那个超标的点就是重心,与已知矛盾。

image-20230824164227688

不理解的可以看图。

所以我们只要让重心作为分治点,那么层数就有保证了。

对于本题,我们考虑答案有三类,就是开头提到的。

对于①,递归即可。

对于②,只需要求出当前点为根其他节点深度,比较一下即可。

对于③,考虑容斥原理,先求出以当前点为根其他节点深度的集合中选择两个数使得深度满足要求的个数(显然这样可能选择两个点在一棵子树中),然后再减去所有以子节点为根的子树的集合(深度值相同)中选择两个数使得深度满足要求的个数,即为所求。

对于在一个数列中选择两个数满足要求,可以先排序,然后使用尺取法即可。

点分治具有最多 \(\lg n\) 层,每层需要排序 \(\lg n\),则复杂度为 \(O(n\lg^2n)\)。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define Ed for(int i=h[x];~i;i=ne[i])
#define Ls(i,l,r) for(int i=l;i<r;++i)
#define Rs(i,l,r) for(int i=l;i>r;--i)
#define Le(i,l,r) for(int i=l;i<=r;++i)
#define Re(i,l,r) for(int i=l;i>=r;--i)
#define L(i,l) for(int i=0;i<l;++i)
#define E(i,l) for(int i=1;i<=l;++i)
#define W(t) while(t--)
#define Wh while

const int N=10010,M=2*N;
int h[N],e[M],ne[M],w[M],idx,n,m,p[N],q[N];//don't forget memset h!
bool st[N];
void add(int a,int b,int c){
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dfs_size(int x,int fa){
    if(st[x])return 0;
    int sum=1;
    Ed{
        int j=e[i];
        if(j==fa)continue;
        sum+=dfs_size(j,x);
    }
    return sum;
}
int find_wc(int x,int fa,int tot,int &u,int &maxn){
    if(st[x])return 0;
    int mx=0,sum=1;
    Ed{
        int j=e[i];
        if(j==fa)continue;
        int tmp=find_wc(j,x,tot,u,maxn);
        if(tmp>mx)mx=tmp;
        sum+=tmp;
    }
    if(tot-sum>mx)mx=tot-sum;
    if(mx<maxn)u=x,maxn=mx;
    return sum;
}
void dfs(int x,int fa,int dep,int &qt){
    if(st[x])return;
    q[qt++]=dep;
    Ed{
        int j=e[i];
        if(j==fa)continue;
        dfs(j,x,dep+w[i],qt);
    }
}
int get(int a[],int t){
    sort(a,a+t);
    int res=0;
    for(int i=t-1,j=-1;~i;--i){
        while(j+1<i&&a[j+1]+a[i]<=m)++j;
        j=j<i-1?j:i-1;
        res+=j+1;
    }
    return res;
}
int calc(int x){
    if(st[x])return 0;
    int res=0,maxn=1e9;
    find_wc(x,-1,dfs_size(x,-1),x,maxn);
    st[x]=1;
    int pt=0;
    Ed{
        int j=e[i],qt=0;
        dfs(j,-1,w[i],qt);
        res-=get(q,qt);
        L(k, qt)p[pt++]=q[k],res+=q[k]<=m;
    }
    res+=get(p,pt);
    //part 1
    Ed{
        int j=e[i];
        res+=calc(j);
    }
    return res;
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("1.in","r",stdin);
    #endif
    while(scanf("%d%d",&n,&m),n||m){
        memset(h,-1,n*4+4);
        memset(st,0,n+1);
        idx=0;
        E(i, n-1){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,c),add(b,a,c);
        }
        printf("%d\n",calc(0));
    }
    return 0;
}

标签:lg,子树,板题,int,分治,mx,define
From: https://www.cnblogs.com/wscqwq/p/17654569.html

相关文章

  • 【算法】分治初步
    目录定义示例快速排序实现第k小三分法归并排序实现定义分治,字面上的解释是“分而治之”,就是把一个问题分成多个的相同或相似的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。示例快速排序把原数组分成左右两段,保证左\(≤\)右,再对左右分别排序。实......
  • CDQ 分治
    定义CDQ分治作为一种思想,主要在解决形如\([l,r]\)区间中找符合要求的点对\((i,j)\)的问题时应用。具体的思想是:先递归解决\([l,mid]\)和\([mid+1,r]\)中的点对;再计算\(l\lei\lemid<mid+1\lej\ler\)的点对\((i,j)\)数量。例题例1:P3810【模板】三维偏序(陌......
  • 点分树(动态点分治) 学习笔记
    模板题题目传送门给定一棵树(带点权),支持以下操作:修改点权。查询到一个点距离\(\lek\)的点的权值和。\(n,T\le10^5\)算法解析前置知识:点分治我们考虑把每次求出的重心和上一层的重心连边,我们就可以得到点分树。这棵树有以下性质:树高为\(\logn\),也就是暴力找LCA的......
  • CDQ分治
    如果现在有一些操作,有些操作会产生贡献,同时里面的情况会依次发生更改,要求我们去维护发生更改后的总贡献。这个问题会使得我们初感很棘手,主要原因在于这是一个动态的问题,当其中一个操作发生变化后会对很多的操作产生影响,导致寻常的数据结构难以维护。而现在引入的CDQ分治可以将一......
  • 分治算法——241. 为运算表达式设计优先级
    分治思路:对于一个算式来说,总是可以根据运算符分为左右两部分算式,接着分别计算结果并合并;每一个结果都是一个数组,包含这个算式的所有可能结果,计算时将左右两部分排列组合;递归的终点是字符串是纯数字(即分到一个算式中只剩下一个数字),直接返回。 比如示例中的2*3-4*5,有下面的......
  • tzoj1471 wall(凸包模板题)
    题目大意n个点构成的城堡,给出每个点的坐标。若要修建距离城堡最近距离为L的城墙,问城墙的最短长度。凸包模板题,用Andrew算法求出凸包然后加上半径为L的圆的周长即可。Andrew算法首先对所有点按照y大小进行升序排序,如果y相同就按照x大小升......
  • 与点对有关的CDQ分治(菜鸟笔记)
    参考文章   首先要说明的是CDQ是一种思想,并且扩展范围很广。   这里主要说的是与点对有关的CDQ。参考文章说了与CDQ主要解决的三大类问题。第一类就是解决和点对有关的问题。主要是给定一个长度为n的序列,然后找出其中满足题意的点对\((i,j)\)。   CDQ的......
  • 分治算法C++
    1、光荣的梦想题目描述】Prince对他在这片陆上维护的秩序感到满意,于是决定启程离开艾泽拉斯。在他动身之前,Prince决定赋予King_Bette最强大的能量以守护世界、保卫这里的平衡与和谐。在那个时代,平衡是个梦想。因为有很多奇异的物种拥有各种不稳定的能量,平衡瞬间即被打破。KB决定求......
  • 浅谈根号分治
    浅谈根号分治一、问题引入  给定一个长度为\(n\)的序列,进行\(m\)次询问。每次询问给出两个数字\(x,y\)。对于每次询问,输出所有下标模除\(x\)等于\(y\)的元素的总和。  对于这个问题,我们发现他要维护的是一段离散的元素的和,而我们平时学的数据结构,如线段树等都只能维护一段......
  • 【学习笔记】线段树分治
    定义线段树分治是一种解决一类有插入、删除和整体查询操作的问题的方法。它是一种离线做法,通过在线段树上记录操作的时间区间来处理修改对询问的影响。每个操作被看作一个时间区间的修改,并在线段树上进行标记。然后通过深度优先搜索(DFS)依次执行这些操作,直到根节点来回答查询,并在......