zoukankan      html  css  js  c++  java
  • P3806 点分治【模板】

    给定一颗有n个点的树,询问树上距离为k的点对是否存在

    点分治适合处理大规模的树上路径信息问题。

    我们先随意选择一个节点作为根节点Root

    所有完全位于其子树中的路径可以分为两种

    一种是经过当前根节点的路径

    一种是不经过当前根节点的路径

    对于经过当前根节点的路径,又可以分为两种

    一种是以根节点为一个端点的路径

    一种是两个端点都不为根节点的路径

    而后者又可以由两条属于前者链合并得到

    所以,对于枚举的根节点Root,我们先计算在其子树中且经过该节点的路径对答案的贡献,再递归其子树对不经过该节点的路径进行求解

    在本题中,对于经过根节点Root的路径,先枚举其所有子节点Ch,以Ch为根计算Ch子树中所有节点到Roto的距离。记节点i到当前根节点Root的距离为Dist(i)。

    tf(d)表示之前处理过的子树中是否存在一个节点v使得Dist(v)=d。

    若一个询问的k满足tf(k-Dist(i)) = true,则存在一条长度为k的路径。

    在计算完Ch子树中所连的边能否成为答案后,我们将这些新的距离加入tf数组中。

    注意在清空tf数组的时候不能直接用Memset,而应将之前占用过的Tf位置加入一个队列中,进行清空。这样才能保证时间复杂度。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+100;
    const int inf=2e9;
    int n,m;
    int q[maxn];
    int rt;
    int size[maxn];
    int Max[maxn];
    int dist[maxn];
    
    bool tf[maxn*100];
    bool ret[maxn];
    bool vis[maxn];
    
    int tot;
    struct node {
        int u,v,w,nxt;
    }edge[maxn<<1];
    int head[maxn];
    void addedge (int u,int v,int w) {
        edge[tot].u=u;
        edge[tot].v=v;
        edge[tot].w=w;
        edge[tot].nxt=head[u];
        head[u]=tot++;
        
        edge[tot].u=v;
        edge[tot].v=u;
        edge[tot].w=w;
        edge[tot].nxt=head[v];
        head[v]=tot++;
    } 
    
    int sum;
    void cal_size (int u,int pre) {
        //计算子树大小
        //计算每个节点的最大子树大小
        size[u]=1;
        Max[u]=0;
        for (int i=head[u];i!=-1;i=edge[i].nxt) {
            int v=edge[i].v;
            if (v==pre) continue;
            if (vis[v]) continue;
            cal_size(v,u);
            Max[u]=max(Max[u],size[v]);
            size[u]+=size[v];
        } 
        Max[u]=max(Max[u],sum-size[u]);
        if (Max[u]<Max[rt]) rt=u; 
    }
    
    int dd[maxn];
    int cnt;
    void cal_dist (int u,int pre) {
        //计算路径
        dd[++cnt]=dist[u];//把每个点距离根节点的路径存到dd里 
        for (int i=head[u];i!=-1;i=edge[i].nxt) {
            int v=edge[i].v;
            if (v==pre) continue;
            if (vis[v]) continue;
            dist[v]=dist[u]+edge[i].w;
            cal_dist(v,u);
        } 
    }
    
    queue<int> tag;
    void Divid (int u,int pre) {
        //分治
        tf[0]=true;
        tag.push(0);
        vis[u]=true;//u已经被分治
        for (int i=head[u];i!=-1;i=edge[i].nxt) {
            int v=edge[i].v;
            if (v==pre) continue;
            if (vis[v]) continue;
            dist[v]=edge[i].w;//v到根节点的路径为w
            cal_dist(v,u);//计算以v为根节点的子树的所有节点到u的距离
            for (int k=1;k<=cnt;k++) for (int j=1;j<=m;j++) {
                if (q[j]>=dd[k]) {
                    //枚举已经记录的所有路径
                    //如果第q次询问大于等于dd[k]
                    //第q次询问的答案或上tf[q[j]-dd[k]]
                    //tf数组表示长度为i的路径是否存在 
                    ret[j]|=tf[q[j]-dd[k]];
                }
            } 
            for (int k=1;k<=cnt;k++) if (dd[k]<maxn*100) {
                tag.push(dd[k]);
                tf[dd[k]]=true;
            }
            cnt=0;
        } 
        while (tag.size()) tf[tag.front()]=false,tag.pop();
        for (int i=head[u];i!=-1;i=edge[i].nxt) {
            int v=edge[i].v;
            if (v==pre) continue;
            if (vis[v]) continue;
            sum=size[v];
            rt=0;
            Max[rt]=inf;
            cal_size(v,u);//先找到以u为根节点的子树的重心 
            cal_size(rt,-1);//以重心为根重新计算子树大小 
            Divid(rt,u);//对u这颗子树进行分治 
        } 
    }
    
    int main () {
        scanf("%d%d",&n,&m);
        for (int i=0;i<=n;i++) head[i]=-1;
        for (int i=1;i<n;i++) {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
        }
        for (int i=1;i<=m;i++) scanf("%d",q+i);
        rt=0;
        Max[rt]=inf;
        sum=n;
        cal_size(1,-1);
        cal_size(rt,-1);
        Divid(rt,-1);
        for (int i=1;i<=m;i++) {
            if (ret[i])
                printf("AYE
    ");
            else
                printf("NAY
    ");
        }
    }
  • 相关阅读:
    Atitit 经济学常见的流派 古典主义与凯恩斯主义
    Atitit 学习方法 体系化学习方法 Excel 科目,分类,专业 三级分类。。 知识点。。 课程就是每一个知识点的详细化。。 比如经济学 类别 专业 xx概论知识点 3、金
    atiitt it学科体系化 体系树与知识点概念大总结.xlsx
    Atitit 减少财政支出普通人如何蹭政府补贴措施 attilax大总结.docx
    Atitit 信用管理概论 attilax学习心得
    Atitit.月度计划日程表 每月流程表v5
    Atitit 企业6大职能 attilax总结
    Atitit 常见每日流程日程日常工作.docx v8 ver ampm imp 签到 am y 天气情况检查 am y 晨会,每天或者隔天 am 每日计划(项目计划,日计划等。 am
    Atitit 财政赤字解决方案
    Atitit 建设自己的财政体系 attilax总结 1.1. 收入理论 2 1.2. 收入分类 2 1.3. 2 1.4. 非货币收入 2 1.5. 2 1.6. 降低期望 2 1.7.
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/13863413.html
Copyright © 2011-2022 走看看