模板·点分治(luogu P3806)

[模板]洛谷·点分治


1、求树的重心

树的重心:若A点的子树中最大的子树的size[] 最小时,A为该树的中心

步骤:

  • 所需变量:siz[x] 表示 x 的子树大小(含自己),msz[x] 表示 其子树中最大的子树的大小,sum表示当前子树所有节点个数,root表示当前子树根节点
  • 处理出siz[x],msz[x]
  • 按最大子树最小的标准处理出root
inline void GetRoot(int x,int fa){
    siz[x]=1;msz[x]=0;siz[x]//表示 x 的子树大小(含自己),msz[x] 表示其子树中最大的子树的大小
    for(Rint i=beg[x];i;i=nex[i]){
        int y=to[i];
        if(y==fa||vis[y])continue;
        GetRoot(y,x);
        siz[x]+=siz[y];
        msz[x]=max(msz[x],siz[y]);
    }
    msz[x]=max(msz[x],sum-siz[x]);//按最大子树最小的标准处理出root
    if(msz[x]<msz[root])root=x;
}
inline void GR_Init(int x,int fa){
    sum=siz[x];//sum表示当前子树所有节点个数
    root=0;//root表示当前子树根节点
    GetRoot(x,fa);
}

2、分而治之

步骤:

  • 处理出经过这个节点的所有所求贡献
  • 减去其子树内不需要被计算但却被计算的贡献
  • 对其子树继续分治
inline void Point_Divide(int x){
    vis[x]=1;
    Calc(x,0,1);//处理出经过这个节点的所有所求贡献
    for(Rint i=beg[x];i;i=nex[i]){
        int y=to[i];
        if(vis[y])continue;
        Calc(y,w[i],-1);//减去其子树内不需要被计算但却被计算的贡献
        GR_Init(y,x);
        Point_Divide(root);//对其子树继续分治
    }
}

3、洛谷P3806点分治模板AC代码

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#define Rint register int
#define mem(a,b) memset(a,(b),sizeof(a))
using namespace std;
typedef long long LL;
template<typename T>inline void read(T &x){
    x=0;T w=1,ch=getchar();
    while(!isdigit(ch)&&ch!='-')ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
    x=x*w;
}
inline void File(){
    freopen("fuck.in","r",stdin);
    freopen("fuck.out","w",stdout);
}

const int maxn=100000+10,inf=0x7f7f7f7f;
int n,m;
int e,beg[maxn],nex[maxn<<1],to[maxn<<1],w[maxn<<1];
int dis[maxn],siz[maxn],dep[maxn],msz[maxn];
int sum,root,cnt;
int ask[maxn],ans[maxn];
bool vis[maxn];

inline void add(int x,int y,int z){
    to[++e]=y;
    nex[e]=beg[x];
    beg[x]=e;
    w[e]=z;
}
inline void GetRoot(int x,int fa){
    siz[x]=1;msz[x]=0;//siz[x]表示 x 的子树大小(含自己),msz[x] 表示其子树中最大的子树的大小
    for(Rint i=beg[x];i;i=nex[i]){
        int y=to[i];
        if(y==fa||vis[y])continue;
        GetRoot(y,x);
        siz[x]+=siz[y];
        msz[x]=max(msz[x],siz[y]);
    }
    msz[x]=max(msz[x],sum-siz[x]);//按最大子树最小的标准处理出root
    if(msz[x]<msz[root])root=x;
}
inline void GR_Init(int x,int fa){
    sum=siz[x];//sum表示当前子树所有节点个数
    root=0;//root表示当前子树根节点
    GetRoot(x,fa);
}
inline void GetDeep(int x,int fa){//处理深度
    dis[++cnt]=dep[x];
    for(Rint i=beg[x];i;i=nex[i]){
        int y=to[i];
        if(vis[y]||y==fa)continue;
        dep[y]=dep[x]+w[i];
        GetDeep(y,x);
    }
}
inline void GD_Init(int x,int val){
    cnt=0;
    dep[x]=val;
    GetDeep(x,0);
}
inline void Calc(int x,int val,int mrk){//计算贡献
    GD_Init(x,val);
    sort(dis+1,dis+cnt+1);
    for(Rint i=1;i<=m;i++){
        for(Rint l=1,r=cnt;l<r;l++){
            while(l<r&&dis[l]+dis[r]>=ask[i]){
                if(dis[l]+dis[r]==ask[i])ans[i]+=mrk;
                r--;
            }
        }
    }
}
inline void Point_Divide(int x){
    vis[x]=1;
    Calc(x,0,1);//处理出经过这个节点的所有所求贡献
    for(Rint i=beg[x];i;i=nex[i]){
        int y=to[i];
        if(vis[y])continue;
        Calc(y,w[i],-1);//减去其子树内不需要被计算但却被计算的贡献
        GR_Init(y,x);
        Point_Divide(root);//对其子树继续分治
    }
}

int main(){
    //File();
    read(n);read(m);
    for(Rint i=1;i<n;i++){
        int x,y,z;read(x);read(y);read(z);
        add(x,y,z);add(y,x,z);
    }
    for(Rint i=1;i<=m;i++)read(ask[i]);
    msz[0]=inf;sum=n;GetRoot(1,0);
    Point_Divide(root);
    for(Rint i=1;i<=m;i++){
        if(ans[i])printf("AYE\n");
        else printf("NAY\n");
    }
    return 0;
}

原文地址:https://www.cnblogs.com/chinhhh/p/8734399.html

时间: 2024-10-05 20:08:42

模板·点分治(luogu P3806)的相关文章

[Luogu] P3806 【模板】点分治1

题目背景 感谢hzwer的点分治互测. 题目描述 给定一棵有n个点的树 询问树上距离为k的点对是否存在. 输入输出格式 输入格式: n,m 接下来n-1条边a,b,c描述a到b有一条长度为c的路径 接下来m行每行询问一个K 输出格式: 对于每个K每行输出一个答案,存在输出“AYE”,否则输出”NAY”(不包含引号) 输入输出样例 输入样例#1: 2 1 1 2 2 2 输出样例#1: AYE 说明 对于30%的数据n<=100 对于60%的数据n<=1000,m<=50 对于100%的数

Luogu 4721 【模板】分治 FFT

还不会这题的多项式求逆的算法. 发现每一项都是一个卷积的形式,那么我们可以使用$NTT$来加速,直接做是$O(n^2logn)$的,我们考虑如何加速转移. 可以采用$cdq$分治的思想,对于区间$[l, r]$中的数,先计算出$[l, mid]$中的数对$[mid + 1, r]$中的数的贡献,然后直接累加到右边去. 容易发现,这样子每一次需要用向量$[l,l + 1, l +  2, \dots, mid]$卷上$g$中$[1, 2, \dots, r - l]$. 时间复杂度$O(nlog^

[题解] Luogu P4721 【模板】分治 FFT

分治FFT的板子为什么要求逆呢 传送门 这个想法有点\(cdq\)啊,就是考虑分治,在算一段区间的时候,我们把他分成两个一样的区间,然后先做左区间的,算完过后把左区间和\(g\)卷积一下,这样就可以算出左区间里的\(f\)对右边的贡献,然后再算右边的就好了. 手玩一组样例吧:g=[0,3,1,2](默认\(g[0] = 0\)) 一开始,只有f[0]=1 f: [1 0|0 0] 然后我们从中间分开来,先算左边的 f: [1|0|0 0] 然后在分下去我们会找到\(f[0]\),就拿这一段和\(

[模板] 点分治

之前搞了一个树的中心,结果当时把点分治给扔下了,现在搞一搞.其实点分治的分治思想很明显,就是把树切成一个个小树,然后在重心的位置再分治就行了. 代码实现有一定困难,但就我觉得前一个函数和树刨的dfs1很像吗,详情见代码. 题干: 题目背景 感谢hzwer的点分治互测. 题目描述 给定一棵有n个点的树 询问树上距离为k的点对是否存在. 输入输出格式 输入格式: n,m 接下来n-1条边a,b,c描述a到b有一条长度为c的路径 接下来m行每行询问一个K 输出格式: 对于每个K每行输出一个答案,存在输

【模板】分治 FFT

题目大意:给定长度为 \(n - 1\) 的序列 \(g\),求 \(f\) 序列,其中 \(f\) 为 \[ f[i]=\sum_{j=1}^{i} f[i-j] g[j] \] 学会了分治 \(fft\). 发现这个式子中也含有卷积,但是这是一个递推式,即:\(f\) 数组是未知的. 考虑分治策略,即:假设已经算出区间 \([l, mid]\) 的 \(f\) 值,现在要计算区间 \([mid + 1, r]\) 的 \(f\). 考虑左半部分对右半部分的贡献,对于 \[x \in [mid

【模板】分治FFT

题意 分析 如果我们已经求得了 f[L],f[L+1] ... f[mid],他们均能对f[mid+1],f[mid+2]...,f[R]产生贡献 对于x ∈ [mid+1,r] f[x] += \sum_{i=L}^{mid}(f[i]*g[x-i]) 等式右边满足卷积模式 具体看代码 代码 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 int n; 5 ll gg[400005],f[4

P4721 【模板】分治 FFT

\(\color{#0066ff}{ 题目描述 }\) 给定长度为 \(n-1\) 的数组 \(g[1],g[2],..,g[n-1]\),求 \(f[0],f[1],..,f[n-1]\),其中 \(f[i]=\sum_{j=1}^if[i-j]g[j]\) 边界为 \(f[0]=1\) .答案模 \(998244353\) . \(\color{#0066ff}{输入格式}\) 第一行一个正整数 \(n\) . 第二行共 \(n-1\) 个非负整数 \(g[1],g[2],..,g[n-1]

点分治+动态点分治

最近好颓废,什么都学不进去... 感谢两篇:AKMer - 浅谈树分治  言简意赅 LadyLex - 点分治&动态点分治小结  讲解+例题,学到很多东西 点分治 动态点分治 ~ 点分治 ~ 经常遇见一类树上的计数题,问的是在某些条件下,选择一些点的方案数 若对于每个点的统计都需要遍历以其为根节点的子树,普通的做法就是$O(n^2)$的,在很多时候是不满足要求的 而这是点分治的专长 点分治是这样进行的: 1. 找到当前树的重心 2. 将重心及重心连出的边全部删去,那么就能将原来的树分割成森林 3

牛客练习赛11 B trie树+拓扑判环 E 分治求平面最近点对

牛客练习赛11 B  假的字符串题意:给定n个字符串,互不相等,你可以任意指定字符之间的大小关系(即重定义字典序),求有多少个串可能成为字典序最小的串,并输出它们. tags:好题 对于一个字符串, 1]如有其它字符串是它的前缀,那肯定不可能.这个直接用字典树处理就可以. 2]但如果以这个字符串为最小,怎么判定其它字符串不会矛盾呢? 其实矛盾的情况详细一点说是: 比如要以  abcd 为最小, 但又有另一个字符串 aba ,这就矛盾了. 对这种情况,在跑字典树的时候,我们对有相同父亲结点的多个儿