点分治

/*
点分治:
我们先随意指定一个根rt,将这棵树转化成有根树
不难发现树上的路径分为两类, 经过根节点rt的路径和包含于rt的某棵子树里(不经过rt)的路径

对于前者, 我们用dis[u]表示结点u到根节点rt的路径长度, 
则u到v的路径长即为dis[u]+dis[v]

对于后者, 既然u到v的路径包含在rt的某个子树内, 那么我们就找到这棵子树的根,再对他求一次第一类路径
这样分治的思想就很明显了

就是把原来的树分成很多小的子树,并对每个子树分别求解第一类路径
点分治过程中,每一层的所有递归过程合计对每个点处理一次 假设共递归T层,则总时间复杂度为O(T*N)
然而,如果树退化成一条链 那么递归层数就是T=n,总时间复杂度为O(N^2)
这样显然不能承受,所以我们要让树的层数经量少 这里就要找树的重心

注意:因为边的权值总和会达到1e8,而查询用到的桶的范围只到1e7,所以如果求出来的路径超过了1e7,则不用记录下来
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn=5e4+10;
typedef long long ll;
int n,m;
int head[maxn],cnt=0;
struct edge{
    int v,next;
    int w;
}e[maxn<<1];
int root,sum;
int size[maxn],dp[maxn],tot=0,dis[maxn];
bool vis[maxn],judge[10000010];
void add(int u,int v,int w){
    e[cnt].v=v;e[cnt].w=w;e[cnt].next=head[u];head[u]=cnt++;
}
int query[maxn],rem[maxn];
bool is_ok[maxn];//第i个询问是否合法
int temp[maxn];
int minNodeSize;

void getroot(int u,int f){
    size[u]=1;dp[u]=0;
    for (int i=head[u];~i;i=e[i].next){
        int v=e[i].v;
        if(v==f || vis[v]) continue;
        getroot(v,u);
        size[u]+=size[v];
        dp[u]=max(dp[u],size[v]);
    }
    dp[u]=max(dp[u],sum-size[u]);
    if(dp[u]<dp[root]) root=u;
}
void getdis(int u,int f){
    rem[++tot]=dis[u];
    for (int i=head[u];~i;i=e[i].next){
        int v=e[i].v,w=e[i].w;
        if(v==f || vis[v]) continue;;
        if(dis[u]+w<=1e7) dis[v]=dis[u]+w;
        getdis(v,u);
    }
}
void cal(int u){
    int p=0;
    for (int i=head[u];~i;i=e[i].next){
        int v=e[i].v,w=e[i].w;
        if(vis[v]) continue;
        tot=0,dis[v]=w;getdis(v,u);
        for (int j=1;j<=tot;j++){
            for (int k=1;k<=m;k++){
                if(rem[j]<=query[k]) is_ok[k]|=judge[query[k]-rem[j]];
            }
        }

        //更新judge
        for (int j=1;j<=tot;j++){
            temp[++p]=rem[j];judge[rem[j]]=true;
        }
    }
    //处理完这颗子树清空judge
    for (int i=1;i<=p;i++){
        judge[temp[i]]=0;
    }
}
void solve(int u){ 
    vis[u]=judge[0]=1;
    cal(u);
    for (int i=head[u];~i;i=e[i].next){
        int v=e[i].v;
        if(vis[v]) continue;
        sum=size[v];
        dp[root=0]=n;
        getroot(v,u);
        solve(root);
    }
}

int main(){
    memset(head,-1,sizeof(head));cnt=0;

    cin>>n>>m;
    for (int i=1;i<n;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w),add(v,u,w);
    }
    for (int i=1;i<=m;i++){
        scanf("%d",&query[i]);
    }
    sum=root=n;
    getroot(1,0);
    solve(root);
    for (int i=1;i<=m;i++){
        printf(is_ok[i]==1?"AYE
":"NAY
");
    }
    return 0;
}
你将不再是道具,而是成为人如其名的人
原文地址:https://www.cnblogs.com/wsl-lld/p/13818737.html