zoukankan      html  css  js  c++  java
  • [ZJOI2015]幻想乡战略游戏

    已更新

    一句话题意:给棵树,有点权有边权

    单点修改点权,维护带权重心

    观察了各位dalao的博客依旧是没看透彻。。。

    最后观察了gxz大佬把代码写完的...

    我们先跑一遍点分治,建点分树(代码里的father数组)

    跑一遍dfs,预处理倍增lca求dis(代码里的fa,depth,dis数组)

    对于每个点,我们维护三个值

    num[x]代表x的点分树中所有节点权值和

    (num_x=sum_{son}val_{son})

    va[x]代表x的点分树的子树中所有节点的权值乘以这个节点到x的距离和

    (va_x=sum_{son}dis(x,son)*val_{son})

    vb[x]代表x的点分树中的子树中所有节点的权值乘上这个点到x点分树父亲的距离和

    (vb_x=sum_{son}dis(father[x],son)*val_{son})

    我们维护这三个数组,就可以快速求出在一个点安营扎寨的费用了

    如下代码:我们在点分树上暴力跳父亲

    每次加上某个点子树内经过这个点到达x的距离

    但是由于下一步跳父亲后会被重复统计

    我们需要再减去这个点所有儿子经过这个点的点分树父亲到达x的距离

    如下代码

    long long calc(int x)
    {
    	long long res = 0;
    	for (int y = x; y != 0; y = father[y])
    	{
    		res += va[y] + num[y] * (long long)getdis(x, y);
    		if (father[y] != 0)
    			res -= vb[y] + num[y] * (long long)getdis(x, father[y]);
    	}
    	return res;
    }
    

    考虑修改:

    直接暴力跳点分树父亲,根据定义修改即可

    考虑查询:

    我们推式子可以发现在答案在原树上是以一种趋势在链上变化的

    所以我们可以在点分树上从点分树的根开始暴力找答案

    我们枚举当前节点原树上所有连边

    找到最优的地方,向那个方向的点分树根跳(因为保证最优解一定在那个方向的子树里)

    如果不存在,说明当前点为根,直接返回答案即可

    链式前向星多开了一个变量rt表示向这个方向如果跳点分树儿子会跳到哪个儿子(参考GXZ代码)

    long long query()
    {
    	int x = root0;
    	long long sb = calc(x), t;
    	while (1)
    	{
    		int y = x;
    		sb = calc(x);
    		for (int i = h[x]; i != 0; i = a[i].ne)
    			if (a[i].rt && (t = calc(a[i].v)) < sb)
    				sb = t, y = a[i].rt;
    		if (x == y)
    			break;
    		x = y;
    	}
    	return sb;
    }
    

    时间复杂度本来是(O(nlog^2n)),但是由于我用的倍增LCA,是(O(nlog^3n))

    代码里偷懒没写快读,还用的倍增LCA,开O2在洛谷上勉强卡过,不开O2T得飞起。。30分。。。

    本机测试,不开O2最大的点9s 开了3s

    乱七八糟的代码

    #include <bits/stdc++.h>
    using namespace std;
    
    struct edge
    {
    	int v, w, ne, rt;
    } a[200010];
    
    int h[100010], tmp;
    int sz[100010], maxw[100010];
    int father[100010];
    bool vis[100010];
    int fa[100010][18], depth[100010], dis[100010];
    long long va[100010], num[100010], vb[100010];
    int n, q, root, sum, root0;
    
    void add(int x, int y, int z)
    {
    	a[++tmp] = (edge){y, z, h[x]};
    	h[x] = tmp;
    }
    
    void getroot(int x, int fa)
    {
    	sz[x] = 1;
    	maxw[x] = 0;
    	for (int i = h[x]; i != 0; i = a[i].ne)
    		if (fa != a[i].v && vis[a[i].v] == false)
    		{
    			getroot(a[i].v, x);
    			sz[x] += sz[a[i].v];
    			maxw[x] = max(maxw[x], sz[a[i].v]);
    		}
    	maxw[x] = max(maxw[x], sum - sz[x]);
    	if (maxw[x] < maxw[root])
    		root = x;
    }
    
    void getsz(int x, int fa)
    {
    	sz[x] = 1;
    	for (int i = h[x]; i != 0; i = a[i].ne)
    		if (fa != a[i].v && vis[a[i].v] == false)
    			getsz(a[i].v, x), sz[x] += sz[a[i].v];
    }
    
    void solve(int x)
    {
    	vis[x] = true;
    	getsz(x, 0);
    	for (int i = h[x]; i != 0; i = a[i].ne)
    		if (vis[a[i].v] == false)
    			sum = sz[a[i].v], root = 0, getroot(a[i].v, 0), a[i].rt = root, father[root] = x, solve(root);
    }
    
    void dfs(int x)
    {
    	for (int i = h[x]; i != 0; i = a[i].ne)
    		if (fa[x][0] != a[i].v)
    			fa[a[i].v][0] = x, depth[a[i].v] = depth[x] + 1, dis[a[i].v] = dis[x] + a[i].w, dfs(a[i].v);
    }
    
    int lca(int x, int y)
    {
    	if (x == 0 || y == 0)
    		return 0;
    	if (depth[x] < depth[y])
    		swap(x, y);
    	int fuck = depth[x] - depth[y];
    	for (int i = 17; i >= 0; i--)
    		if (fuck & (1 << i))
    			x = fa[x][i];
    	if (x == y)
    		return x;
    	for (int i = 17; i >= 0; i--)
    		if (fa[x][i] != fa[y][i])
    			x = fa[x][i], y = fa[y][i];
    	return fa[x][0];
    }
    
    int getdis(int x, int y)
    {
    	return dis[x] + dis[y] - 2 * dis[lca(x, y)];
    }
    
    long long calc(int x)
    {
    	long long res = 0;
    	for (int y = x; y != 0; y = father[y])
    	{
    		res += va[y] + num[y] * (long long)getdis(x, y);
    		if (father[y] != 0)
    			res -= vb[y] + num[y] * (long long)getdis(x, father[y]);
    	}
    	return res;
    }
    
    long long query()
    {
    	int x = root0;
    	long long sb = calc(x), t;
    	while (1)
    	{
    		int y = x;
    		sb = calc(x);
    		for (int i = h[x]; i != 0; i = a[i].ne)
    			if (a[i].rt && (t = calc(a[i].v)) < sb)
    				sb = t, y = a[i].rt;
    		if (x == y)
    			break;
    		x = y;
    	}
    	return sb;
    }
    
    int main()
    {
    	scanf("%d%d", &n, &q);
    	for (int x, y, z, i = 1; i < n; i++)
    		scanf("%d%d%d", &x, &y, &z), add(x, y, z), add(y, x, z);
    	maxw[0] = n, sum = n, getroot(1, 0), root0 = root, solve(root);
    	dfs(1);
    	for (int j = 1; j <= 17; j++)
    		for (int i = 1; i <= n; i++)
    			fa[i][j] = fa[fa[i][j - 1]][j - 1];
    	for (int x, chenge, i = 1; i <= q; i++)
    	{
    		scanf("%d%d", &x, &chenge);
    		for (int y = x; y != 0; y = father[y])
    		{
    			va[y] += chenge * (long long)getdis(x, y), num[y] += chenge;
    			if (father[y] != 0)
    				vb[y] += chenge * (long long)getdis(x, father[y]);
    		}
    		printf("%lld
    ", query());
    	}
    	return 0;
    }
    
  • 相关阅读:
    XMLHttpRequest简介
    BC30138: 无法在路径“C:\WINDOWS\TEMP\”中创建临时文件: 拒绝访问。
    开机explorer无法启动,无法进入桌面
    选择DataGrid中的CheckBox控件后该行背景变色
    CSS菜单制作工具
    ScriptX打印控件的使用
    JS实时预览上传图片缩略图
    readyState的五种状态详解
    xmlHttpRequest的status的值的含义
    C#调用word打印
  • 原文地址:https://www.cnblogs.com/oier/p/10235898.html
Copyright © 2011-2022 走看看