• 点分治


    点分治

    树的重心

    到所有点的距离之和最小

    到其他点的最大距离最小

    一棵树最多有两个重心,且相邻

    两棵树通过一条边相连,新的重心在两棵树重心的链上

    添加或删除一个节点,树的重心最多只移一条边的位置

    求法

    void getroot(int u, int f) { //f 是 father 节点
        // 找质心
        sz[u] = 1;
        int mxchild = 0; //子树的最大节点数
        for(int i = head[u]; i != -1; i = e[i].nxt ) {
            int v = e[i].to;
            if(v != f) {
                getroot(v, u);
                sz[u] += sz[v];
                mxchild = max(mxchild, sz[v]);
            }
        }
        int tmp = max(mxchild, subtreesize - sz[u]); //父子树的最大节点个数
        if(tmp < MX) {
            MX = tmp;
            rt = u;
        }
    }
    
    

    点分治

    感觉洛谷点分治模板 的题解写的不是很清楚

    这篇写的不错

    大概知道是怎么一回事了

    点分治,一种针对可带权树上简单路径统计问题的算法。

    一种带优化的暴力,加上一点容斥

    每次找树的重心进行分治

    把路径分为两种

    • 经过 (t) 的路径
    • 不经过 (t) 的路径

    统计出从 (t) 出发的路径,对这些路径两两合并,就可以得到树上的简单路径。

    但是这些路径中有些合并是不合法的。

    贴一份那篇博客的代码

    POJ-1741

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define F(i,a,b) for(int i=a;i<=(b);++i)
    #define eF(i,u) for(int i=h[u];i;i=nxt[i])
    using namespace std;
    const int INF = 0x3f3f3f3f;
    
    int n, k, Ans;
    
    int h[10001], nxt[20001], to[20001], w[20001], tot;
    inline void ins(int x, int y, int z) { nxt[++tot] = h[x]; to[tot] = y; w[tot] = z; h[x] = tot; }
    
    bool vis[10001];
    int Root, Tsiz, siz[10001], wt[10001];
    int arr[10001], cnt;
    
    void GetRoot(int u, int f) {
    	siz[u] = 1; wt[u] = 0;
    	eF(i, u) if (to[i] != f && !vis[to[i]])
    		GetRoot(to[i], u), siz[u] += siz[to[i]], wt[u] = max(wt[u], siz[to[i]]);
    	wt[u] = max(wt[u], Tsiz - siz[u]);
    	if (wt[Root] > wt[u]) Root = u;
    }
    
    void Dfs(int u, int D, int f) {
    	arr[++cnt] = D;
    	eF(i, u) if (to[i] != f && !vis[to[i]]) Dfs(to[i], D + w[i], u);
    }
    
    int calc(int u, int D) {
    	cnt = 0; Dfs(u, D, 0); int l = 1, r = cnt, sum = 0;
    	sort(arr + 1, arr + cnt + 1);
    	for (;; ++l) {
    		while (r && arr[l] + arr[r] > k) --r;
    		if (r < l) break;
    		sum += r - l + 1;
    	}
    	return sum;
    }
    
    void DFS(int u) {
    	Ans += calc(u, 0); vis[u] = 1;
    	eF(i, u) if (!vis[to[i]]) {
    		Ans -= calc(to[i], w[i]);
    		Root = 0, Tsiz = siz[to[i]], GetRoot(to[i], 0);
    		DFS(Root);
    	}
    }
    
    int main() {
    	int x, y, z;
    	while (~scanf("%d%d", &n, &k) && n && k) {
    		tot = Ans = 0; memset(vis, 0, sizeof vis); memset(h, 0, sizeof h);
    		F(i, 2, n) scanf("%d%d%d", &x, &y, &z), ins(x, y, z), ins(y, x, z);
    		wt[0] = INF; Tsiz = n; GetRoot(1, 0);
    		DFS(Root);
    		printf("%d
    ", Ans - n);
    	}
    	return 0;
    }
    

    聪聪可可

    输出树上长度是3的倍数的数目与所有路径之比

    不是(C_n^2)(n^2)

    #include<bits/stdc++.h>
    using namespace std;
    
    #define repE(i,u) for(int i = head[u];i;i=nxt[i])
    #define rep(i,a,b) for(int i = a;i <= b;i++)
    
    const int maxn = 3e5 + 10;
    int head[maxn], w[maxn], to[maxn], nxt[maxn], tot;
    int n, k, m;
    int MX;
    bool vis[maxn];
    int Tsz, sz[maxn], mx[maxn], Root;
    int cnt, dis[maxn];
    map<int, int>ans;
    
    int p_sum;
    
    void addEdge(int x, int y, int z) {
    	nxt[++tot] = head[x]; to[tot] = y; w[tot] = z; head[x] = tot;
    }
    
    void Getroot(int u, int f) {
    	sz[u] = 1, mx[u] = 0;
    	repE(i, u) {
    		if (to[i] != f and !vis[to[i]]) {
    			Getroot(to[i], u);
    			sz[u] += sz[to[i]];
    			mx[u] = max(mx[u], sz[to[i]]);
    		}
    		mx[u] = max(mx[u], Tsz - sz[u]);
    		if (mx[Root] > mx[u]) {
    			Root = u;
    		}
    	}
    }
    void dfs(int u, int len, int f) {
    	dis[cnt++] = len;
    	repE(i, u) {
    		if (to[i] != f and !vis[to[i]]) {
    			dfs(to[i], len + w[i], u);
    		}
    	}
    }
    
    void cal(int u, int w, int val) {
    	cnt = 0; dfs(u, w, 0);
    
    	int p[3] = { 0 };
    	for (int i = 0; i < cnt; i++) {
    		p[dis[i] % 3]++;
    	}
    	p_sum += val * (p[0] * p[0] + 2 * p[1] * p[2]);
    }
    void DFS(int u) {
    	cal(u, 0, 1); vis[u] = 1;
    	repE(i, u) {
    		if (!vis[to[i]]) {
    			cal(to[i], w[i], -1);
    			Root = 0, Tsz = sz[to[i]], Getroot(to[i], 0);
    			DFS(Root);
    		}
    	}
    }
    
    int query[maxn];
    int gcd(int a, int b) { return a % b == 0 ? b : gcd(b, a % b); }
    int main() {
    	scanf("%d", &n);
    	int x, y, z;
    	rep(i, 1, n - 1) scanf("%d%d%d", &x, &y, &z), addEdge(x, y, z), addEdge(y, x, z);
    
    	Tsz = n; mx[0] = 0x3f3f3f3f;
    	Getroot(1, 0);
    	DFS(Root);
    	int g = gcd(p_sum, n * n);
    	printf("%d/%d
    ", p_sum / g, n * n / g);
    }
    
  • 相关阅读:
    【转】MFC中png格式图片贴图的实现
    【转】Windows 中不规则窗体的编程实现
    【转】MFC添加背景图片方法的三种方法
    【转】mfc win7获得管理员权限 使用WIN7风格 使用当前系统风格
    【转】双缓冲讲解及界面贴图
    【转】[内核/驱动]驱动中获取进程全路径和注册表全路径
    【转】Visual C++中DDB与DIB位图编程全攻略(转)
    【转】vs2010中添加splashScreen
    【转】一个在内存里搜索QQ号码的源码,源自看雪论坛
    输出JSON
  • 原文地址:https://www.cnblogs.com/sduwh/p/13734115.html
Copyright © 2020-2023  润新知