淀(点)粉(分)质(治)的百来种做法

$large{先说好,因为本人比较菜,只能口胡一下板子了,有什么优(毒)秀(瘤)的题后期再更吧……}$

淀粉质,是一种很好吃的高能量食品,但如果不及时消化,就会消化不良(大雾)

来自百度百科的介绍

板子题:$Large{Luogu}P3806$

首先考虑一下,不管怎么做,总要从脑袋到脚,跑个树高,而这又是个无根树

于是我们就抛出了我们树的重心:

- 利用$DFS$找到每个节点下面子树顺序的大小(包括自己),同时用整棵树的大小减去这个值求出这个节点另一边的大小(不理解的话可以画图理解一下QQwQ)

如上图,当我们在计算$2$节点时会计算其子树大小(为4),并用总数减子树大小(为6),再取$max$

像这样不停的找一个两边比较平衡的节点就可以找到中心,代码如下:

inline void FocFind(int pos,int fa)
{
    maxn[pos]=0;size[pos]=1;
    for(int i=head[pos];i;i=edge[i].next)
        if(!vis[edge[i].to]&&edge[i].to!=fa)
        {
            FocFind(edge[i].to,pos);
            size[pos]+=size[edge[i].to];
            maxn[pos]=max(maxn[pos],size[edge[i].to]);
        }
    maxn[pos]=max(maxn[pos],sum-size[pos]);
    if(maxn[foc]>maxn[pos]) foc=pos;
}

(初始化我就不写了,反正很水)

通过找树的重心,我们完美的将每次要分治的树抖成了$Log$的高度(想想都有点小兴奋呢)

题目要求每次询问两点之间长度为$c$的路径是否存在,因此我们可以先预处理出存在的路径长度。

考虑每次分治一棵树时我们应该做什么。

显然因为我们是分治,所以要处理出当前树的根(即重心)到每个点之间的距离并存下来(不用考虑顺序),同时开一个$judge​$来记录一种距离是否出现。跑完当前树的根的一棵子树后跑一遍距离,检查一下每个距离和$c​$的差是否出现过就可以了,如果看不懂可以自行画图理解一下。

上代码:

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int f=1,w=0;char x=0;
    while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
    while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
    return w*f;
}
int check[10000001],judge[10000001];
int head[400004],num_edge,n,m,sum,Q[100001],now[100001];
int size[100001],foc,dis[100001],vis[100001],road[100001],maxn[100001];
struct Edge{int next,to,dis;} edge[400004];
inline void add(int from,int to,int dis)
{
    edge[++num_edge].next=head[from];
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    head[from]=num_edge;
}
inline void FocFind(int pos,int fa)
{
    maxn[pos]=0;size[pos]=1;
    for(int i=head[pos];i;i=edge[i].next)
        if(!vis[edge[i].to]&&edge[i].to!=fa)
        {
            FocFind(edge[i].to,pos);
            size[pos]+=size[edge[i].to];
            maxn[pos]=max(maxn[pos],size[edge[i].to]);
        }
    maxn[pos]=max(maxn[pos],sum-size[pos]);
    if(maxn[foc]>maxn[pos]) foc=pos;
}
inline void GetDis(int pos,int fa)
{
    road[++road[0]]=dis[pos];
    for(int i=head[pos];i;i=edge[i].next)
        if(edge[i].to!=fa&&!vis[edge[i].to])
        {
            dis[edge[i].to]=dis[pos]+edge[i].dis;
            GetDis(edge[i].to,pos);
        }
}
inline void AnsForSon(int pos)
{
    int tot=0;
    for(int i=head[pos];i;i=edge[i].next)
        if(!vis[edge[i].to])
        {
            dis[edge[i].to]=edge[i].dis;
            road[0]=0;
            GetDis(edge[i].to,pos);
            for(int j=road[0];j;j--)
                for(int k=1;k<=m;k++)
                    if(Q[k]-road[j]>=0)
                        check[k]|=judge[Q[k]-road[j]];
            for(int j=road[0];j;j--)
                now[++tot]=road[j],judge[road[j]]=1;
        }
    for(int i=1;i<=tot;i++) judge[now[i]]=0;
}
inline void Solve(int pos)
{
    vis[pos]=judge[0]=1;AnsForSon(pos);
    for(int i=head[pos];i;i=edge[i].next)
        if(!vis[edge[i].to])
        {
            sum=size[edge[i].to];maxn[foc=0]=0x3f3f3f3f;
            FocFind(edge[i].to,0);Solve(foc);
        }
}
int main(){
    n=read(),m=read();
    for(int i=1;i<n;i++)
    {
        int u=read(),v=read(),d=read();
        add(u,v,d),add(v,u,d);
    }
    for(int i=1;i<=m;i++) Q[i]=read();
    maxn[foc]=sum=n;
    FocFind(1,0);
    Solve(foc);
    for(int i=1;i<=m;i++)
        if(check[i]) puts("AYE");
        else puts("NAY");
}

 注意,点分治的过程中相当于弱化了原来的树的结构,因此在理解时不要太过在意父子关系,主要理解$vis$就好

原文地址:https://www.cnblogs.com/wo-shi-zhen-de-cai/p/10463378.html