【Luogu】P3806点分治模板(点分治)

  题目链接

  wc听不懂lca讲的高等数学专场(一个字都听不懂),然后就自学了点分治。

  点分治就是我先处理完跟根有关的东西,然后把根标记掉,把原树拆成若干个联通块,然后分别对每个联通块(每个小树)搞一模一样的操作。

  然后要每次求重心,因为点分治复杂度跟递归深度有关。

  本题判断的时候偷懒用map,其实自己写的splay也快不到哪里去的样子。qwq。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<cstdlib>
#include<map>
#define maxn 200020
using namespace std;
inline long long read(){
    long long num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch==‘-‘)    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-‘0‘;
        ch=getchar();
    }
    return num*f;
}

struct Edge{
    int next,to,val;
}edge[maxn*3];
int head[maxn],num;
inline void add(int from,int to,int val){
    edge[++num]=(Edge){head[from],to,val};
    head[from]=num;
}

map<int,bool>ext;

bool tag[maxn];
int size[maxn];
int mxn[maxn];
int stack[maxn],top;
int d[maxn];
int w[maxn];
int root=0;

void getroot(int x,int fa,int lim){
    int ret=0;size[x]=1;
    for(int i=head[x];i;i=edge[i].next){
        int to=edge[i].to;
        if(to==fa||tag[to])    continue;
        getroot(to,x,lim);
        size[x]+=size[to];
        if(ret<size[to])    ret=size[to];
    }
    if(ret<lim-size[x])    ret=lim-size[x];
    mxn[x]=ret;
    if(ret<mxn[root]||root==0)    root=x;
}

void depfind(int x,int fa,int dst){
    stack[++top]=dst;
    for(int i=head[x];i;i=edge[i].next){
        int to=edge[i].to;
        if(to==fa||tag[to])    continue;
        depfind(to,x,dst+edge[i].val);
    }
    return;
}

int getsize(int x,int fa){
    int ans=1;
    for(int i=head[x];i;i=edge[i].next){
        int to=edge[i].to;
        if(to==fa||tag[to])    continue;
        ans+=getsize(to,x);
    }
    return ans;
}

void dfs(int x,int lim,int m){
    root=0;
    getroot(x,x,lim);
    tag[root]=1;
    ext.clear();ext[0]=1;
    for(int i=head[root];i;i=edge[i].next){
        int to=edge[i].to;
        if(tag[to])    continue;
        top=0;
        depfind(to,to,edge[i].val);
        sort(stack+1,stack+top+1);
        for(int j=1;j<=m;++j){
            if(w[j])    continue;
            for(int k=1;k<=top;++k){
                int now=d[j]-stack[k];
                if(ext[now]==1)    w[j]=1;
            }
        }
        for(int j=1;j<=top;++j)    ext[stack[j]]=1;
    }
    int now=root;
    for(int i=head[now];i;i=edge[i].next){
        int to=edge[i].to;
        if(tag[to])    continue;
        int ret=getsize(to,to);
        dfs(to,ret,m);
    }
}

int main(){
    int n=read(),m=read();
    for(int i=1;i<n;++i){
        int x=read(),y=read(),z=read();
        add(x,y,z);
        add(y,x,z);
    }
    for(int i=1;i<=m;++i)    d[i]=read();
    dfs(1,n,m);
    for(int i=1;i<=m;++i)
        if(w[i]==1)    printf("AYE\n");
        else        printf("NAY\n");
    return 0;
}
/*
13 10
1 2 1
1 4 2
4 3 5
4 5 11
5 6 4
5 7 2
4 8 7
1 10 2
9 10 9
1 11 4
11 13 5
11 12 6
12
11
10
9
8
22
13
14
15
18
*/

原文地址:https://www.cnblogs.com/cellular-automaton/p/8419515.html

时间: 2024-07-31 22:27:05

【Luogu】P3806点分治模板(点分治)的相关文章

点分治+动态点分治

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

模板&#183;点分治(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 的子树大小(含自

[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%的数

COGS 2479. [HZOI 2016]偏序 [CDQ分治套CDQ分治 四维偏序]

传送门 给定一个有n个元素的序列,元素编号为1~n,每个元素有三个属性a,b,c,求序列中满足i<j且ai<aj且bi<bj且ci<cj的数对(i,j)的个数. 对于100%的数据,1<=n<=50000,保证所有的ai.bi.ci分别组成三个1~n的排列. $CDQ$分治套$CDQ$分治也不是很难嘛 对于本题,设四维$a,b,c,d$ $Sort\ at\ a$ $CDQ(l,r)$ $\quad CDQ(l,mid)$ $\quad CDQ(mid+1,r)$ $\

[Luogu 3701] 「伪模板」主席树

[Luogu 3701] 「伪模板」主席树 <题目链接> 这是一道网络流,不是主席树,不是什么数据结构,而是网络流. 题目背景及描述都非常的暴力,以至于 Capella 在做此题的过程中不禁感到生命流逝. S 向 byx 的树中的每一个人连有向边,手气君的树中的每一个人向 T 连有向边,边权为这个人的寿命.统计同一棵树中的膜法师数量 x.如果一个人是主席,那么边权要加上 x.(续得好啊) 然后,如果 byx 树中的一个点 i 能赢手气君树中的点 j,那么连 i->j,边权为 1. 跑最大

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]\),就拿这一段和\(

点分治模板题

https://www.luogu.org/problem/P3806 #include<bits/stdc++.h> using namespace std; const int maxn=2e4+10; int head[maxn],ver[maxn],nxt[maxn],edge[maxn]; int tot; int vis[maxn]; // 分治时用来标记哪个重心已经使用过 int ans,size[maxn],root,sum; // 求重心时使用,ans记录目前标记重心最小子树

luoguP3806 【模板】点分治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%的数