zoukankan      html  css  js  c++  java
  • P4178 Tree(点分治)

    相比两个点的距离相加等于k, 等于k的倍数而言

    这题稍微转换一下思路即可

    利用容斥原理去掉重复计算的点对

    点击查看代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
    using ll = long long;
    
    constexpr int MAXN = 4e4 + 7;
    
    int h[MAXN], e[MAXN << 1], ne[MAXN << 1], w[MAXN << 1], Size[MAXN], mx[MAXN], idx, root, S, subSize;
    int ans, dis[MAXN], tot;
    int dd[MAXN];
    bool vis[MAXN]; //当前点是否被删除
    
    int n, m, k, cnt;
    void add(int u, int v, int c = 0)
    {
    	++idx;
    	e[idx] = v;
    	w[idx] = c;
    	ne[idx] = h[u];
    	h[u] = idx;
    }
    
    void getroot(int x, int fa) //以下是树的重心模板
    {
    	Size[x] = 1;
    	mx[x] = 0;
    	for (int i = h[x]; i; i = ne[i])
    	{
    		int j = e[i];
    		if (j != fa && !vis[j])
    		{
    			getroot(j, x);
    			Size[x] += Size[j];
    			mx[x] = max(mx[x], Size[j]);
    		}
    	}
    	mx[x] = max(mx[x], S - Size[x]);
    	if (mx[x] < mx[root])
    		root = x;
    }
    void getdis(int x, int fa)
    {
    	dd[++cnt] = dis[x];
    	for (int i = h[x]; i; i = ne[i])
    	{
    		int j = e[i];
    		if (j != fa && !vis[j])
    		{
    			dis[j] = dis[x] + w[i];
    			getdis(j, x); //遍历以j节点为根节点的整颗子树
    		}
    	}
    }
    
    int solve(int x)
    {
    	int ans = 0;
    	cnt = 0;	  //将上次保存点的距离的计数清空(
    	getdis(x, 0); //以x为根统计一遍所有子节点的距离
    
    	sort(dd + 1, dd + cnt + 1); //排序后用双指针从头从尾开始尺取
    	for (int u = 1, v = cnt; u <= v;)
    	{
    		if (dd[u] + dd[v] <= k)
    		{
    			ans += v - u, ++u;
    		}
    		else
    			--v;
    	}
    	return ans;
    }
    void div(int x)
    {
    	vis[x] = true; //删除当前根节点
    	dis[x] = 0;
    	ans += solve(x); //路径计数!
    	for (int i = h[x]; i; i = ne[i])
    	{
    		int j = e[i];
    		if (!vis[j]) // j节点被删除则不搜索, 因为已经被搜索过
    		{
    			dis[j] = w[i];
    			ans -= solve(j);
    			root = 0;
    			S = Size[j];	 //供找重心使用
    			mx[0] = Size[j]; //将最重的儿子大小暂时设为以j为根节点的子树大小, 留给后面找重心使用
    			getroot(j, 0);
    			getroot(root, 0);
    			div(root); //找到重心, 将重心作为根节点开始搜子树(保证时间复杂度最优)
    		}
    	}
    }
    
    int main()
    {
    	int u, v, c;
    
    	IOS;
    	cin >> n;
    	for (int i = 1; i < n; ++i)
    	{
    		cin >> u >> v >> c;
    		add(u, v, c);
    		add(v, u, c);
    	}
    	cin >> k;
    	root = 0;
    	mx[0] = n;
    	S = n;
    	getroot(1, 0);
    	getroot(root, 0);
    	div(root);
    	cout << ans << '
    ';
    
    	return 0;
    }
  • 相关阅读:
    Charles手机端抓包--证书
    新浪微盘批量下载
    [3140] Sublime Text 2.x, 3.x 通用注册码、密钥集合
    栈的数组实现
    栈的链表实现
    基数排序
    多项式相乘-数组实现
    最大子序列和问题的四种算法
    链表的实现
    时间同步算法与Simple Ring-based election algorithm算法分析-转载
  • 原文地址:https://www.cnblogs.com/daremo/p/15488222.html
Copyright © 2011-2022 走看看