zoukankan      html  css  js  c++  java
  • 「点分治」学习笔记

    点分治能够快速处理树上路径问题。

    在考虑路径问题时,可以利用分治的思想。选出一个根节点,先考虑所有经过根节点的路径。路径的起点和终点分别在根节点的两个子树上。解决之后,要解决的就是各个子树的路径问题了。我们再找出每个子树的根节点,然后一样递归处理即可。

    问题在于应该选哪个节点作为子树的根节点。最优方法是选取树的重心。因为重心有一个性质,它的各个子树的大小都不超过总大小的一半。这样每一次都分为若干个不超过一半的子问题,就变成了分治算法。分治次数为$O( log n)$。如果想要达成$O(n log n)$的复杂度,那对于每个节点的计算就要$O(n)$完成

    Code

    有一些细节需要注意。

    /*DennyQi 2019*/
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <queue>
    using namespace std;
    const int N = 10010;
    inline int read(){
        int x(0),w(1); char c = getchar();
        while(c^'-' && (c<'0' || c>'9')) c = getchar();
        if(c=='-') w = -1, c = getchar();
        while(c>='0' && c<='9') x = (x<<3)+(x<<1)+c-'0', c = getchar(); 
        return x*w;
    }
    int n,m,x,y,z,S,rt,cnt,test[N],vis[N],sz[N],maxp[N],judge[10000010],rem[N],q[N],Q[N];
    int head[N],nxt[N<<1],to[N<<1],cost[N<<1],cnte;
    inline void add(int u, int v, int w){
        to[++cnte] = v;
        cost[cnte] = w;
        nxt[cnte] = head[u];
        head[u] = cnte;
    }
    void getrt(int u, int Fa){
        sz[u] = 1;
        maxp[u] = 0;
        for(int i = head[u]; i; i = nxt[i]){
            if(to[i] == Fa) continue;
            if(vis[to[i]]) continue;
            getrt(to[i],u);
            sz[u] += sz[to[i]];
            maxp[u] = max(maxp[u],sz[to[i]]);
        }
        maxp[u] = max(maxp[u],S-sz[u]);
        if(maxp[u] < maxp[rt]) rt = u;
    }
    void getdis(int u, int Fa, int d){
        rem[++cnt] = d;
        for(int i = head[u]; i; i = nxt[i]){
            if(to[i] == Fa) continue;
            if(vis[to[i]]) continue;
            getdis(to[i],u,d+cost[i]);
        }
    }
    inline void calc(int u){
        int p = 0;
        for(int i = head[u]; i; i = nxt[i]){
            if(vis[to[i]]) continue;
            cnt = 0;
            getdis(to[i],u,cost[i]);
            for(int j = 1; j <= cnt; ++j){
                for(int k = 1; k <= m; ++k){
                    if(Q[k] >= rem[j]){
                        test[k] |= judge[Q[k]-rem[j]];
                    }
                }
            }
            for(int j = 1; j <= cnt; ++j){
                q[++p] = rem[j];
                judge[rem[j]] = 1;
            }
        }
        for(int i = 1; i <= p; ++i) judge[q[i]] = 0;
    }
    void solve(int u){
        vis[u] = 1;
        judge[0] = 1;
        calc(u);
        for(int i = head[u]; i; i = nxt[i]){
            if(vis[to[i]]) continue;
            S = sz[to[i]];
            maxp[rt = 0] = 0x3f3f3f3f;
            getrt(to[i],-1);
            solve(rt);
        }
    }
    int main(){
        // freopen("file.in","r",stdin);
        n = read(), m = read();
        for(int i = 1; i < n; ++i){
            x = read(), y = read(), z = read();
            add(x,y,z);
            add(y,x,z);
        }
        for(int i = 1; i <= m; ++i) Q[i] = read();
        S = n;
        maxp[rt] = 0x3f3f3f3f;
        getrt(1,-1);
        solve(rt);
        for(int i = 1; i <= m; ++i){
            if(test[i]) printf("AYE
    "); else printf("NAY
    ");
        }
        return 0;
    }

    带权树上长度为K的路径是否存在(洛谷P3806):点分治时记录每个根节点出发能达到的距离,开桶记录即可。

    带权树上长度为3的倍数的路径条数(洛谷P2634):只需要记录一下模三的剩余系中的路径条数就好了。

    带权树上长度为K的路径的最小边数(洛谷P4149):只需要维护的距离的时候多维护一个边数就好了。

    带权树上长度$leq$K的路径条数(洛谷P4178):暴力统计前缀和会被菊花图卡爆。用树状数组维护,多一只$mathrm{log}$。

  • 相关阅读:
    uniapp开发注意事项
    uniapp生成海报带二维码及保存
    严格模式的this
    数据类型
    短路特性
    第九周程序改错
    矩阵转置
    二分法求根
    三天打鱼两天晒网
    LeetCode7
  • 原文地址:https://www.cnblogs.com/qixingzhi/p/11118212.html
Copyright © 2011-2022 走看看