zoukankan      html  css  js  c++  java
  • 【题解】$test0628$ 仓库选址

    ybt 1771 仓库选址

    题目描述

    喵星系有 (n) 个星球,星球以及星球间的航线形成一棵树。

    从星球 (a) 到星球 (b) 要花费 ([dis(a,b)XorM])秒。((dis(a,b)) 表示 (a,b) 间的航线长度,(Xor) 为位运算中的异或)

    为了给仓库选址,pf想知道,星球 (i)(1≤i≤n))到其他所有星球花费的时间之和。

    (\)


    (\)

    (Solution)

    这道题其实一点都不难啊!!!就是一个裸换根 (dp) 加一点点二进制小技巧,网上的题解太高大上了,蒟蒻瑟瑟发抖 (kk)

    首先发现,若 (m = 0) 那么就是个裸换根 (dp)

    具体的就不写了,连我这种蒟蒻都会那各位dalao肯定都会!(主要是懒

    考虑加上异或 (m)

    观察数据范围,发现 (m <= 15),也就是说,异或了 (m) 顶多修改二进制下后面 (4)

    于是另外设 (g[i][j]) 表示到 (i) 的点的路径中,有多少条是二进制下后面 (4) 位为 (j)

    然后我们就只要在最最最后,加上这样一句,$$ans[i] += ((j xor m) - j) * g[i][j]$$

    就是答案啦!是不是超简单!!

    这一句话是什么意思呢?

    假设有一条到 (i) 的路径长为 (x)(x) 的二进制下后 (4) 位为 (j) ,由于顶多更改最后 (4) 位,所以只有 (j) 部分被更改

    在题意里要加进去的贡献应该是 (j xor m) ,但是现在 (ans[i]) 里存的是 (j) ,所以我们要加上一个 (j xor m) 减去一个 (j)

    简单来说就是这样$$j + (j xor m - j) = j xor m$$

    那么这优化在了哪里呢?

    实际上这就相当于我们对于每一条路径都先求出长度,然后更改,但是这样就是 (O(n^2)) 了,于是我们把所有能够一起更改的(也就是后四位一样的)统计一下,在最后一起更改,复杂度就降为 (O(nm)) 辣!

    再次感慨一下这道题做法,太!神!力!

    完结撒花✿✿ヽ(°▽°)ノ✿

    (\)


    (\)

    (Code)

    好吧原来这道题难点在于代码实现???重构了代码的蒟蒻哭力——

    可能代码和上面讲的有些偏差,具体代码里解释

    #include<bits/stdc++.h>
    #define F(i, x, y) for(int i = x; i <= y; ++ i)
    using namespace std;
    int read();
    const int N = 1e5 + 5;
    const int mod = 16;
    int n, m, u, v, e;
    int g[N][16], dp[N];
    int head[N], cnt, ver[N << 1], edge[N << 1], nxt[N << 1];
    void add(int x, int y, int z){ver[++ cnt] = y, edge[cnt] = z, nxt[cnt] = head[x], head[x] = cnt;}
    void dfs1(int x, int fa)
    {
       g[x][0] = 1;
       for(int i = head[x]; i; i = nxt[i])
          if(ver[i] != fa)
          {
             dfs1(ver[i], x), dp[x] += dp[ver[i]];//这里的dp求的是i子树到i的距离和
             F(j, 0, 15) g[x][(j + edge[i]) % mod] += g[ver[i]][j], dp[x] += g[ver[i]][j] * edge[i];
             /*对于当前这条边,会被贡献size[son]这么多次,而g[son]的总和就是size[son]*/
          }
    }
    void dfs2(int x, int fa)
    {
       for(int i = head[x]; i; i = nxt[i])
          if(ver[i] != fa)
          {
             int tmp[16] = {0}, size = 0;
             F(j, 0, 15) 
                tmp[(j + edge[i]) % mod] += g[x][j] - g[ver[i]][((j - edge[i]) % mod + mod) % mod], size += g[ver[i]][j];
                /*由fa往i转移时要用g[fa]减掉那些i子树中的链*/
             dp[ver[i]] = dp[x] + (n - size * 2)  * edge[i];
             /*可以看成n-size-size,n-size是有这么多个点要经过这条边,还要减一个size是因为dp[x]中包含了size条这个边,要减去*/
             F(j, 0, 15) g[ver[i]][j] += tmp[j];
             dfs2(ver[i], x);
          }
    }
    int main()
    {
       n = read(), m = read();
       F(i, 1, n - 1) u = read(), v = read(), e = read(), add(u, v, e), add(v, u, e);
       dfs1(1, 0), dfs2(1, 0);
       F(i, 1, n) F(j, 0, 15) dp[i] += ((j ^ m) - j) * (g[i][j] - (j == 0));
       F(i, 1, n) printf("%d
    ", dp[i]);
       return 0;
    }
    /*--------------- Bn_ff 2020.7.2 ybt1771 ---------------*/
    int read()
    {
    	int x = 0, f = 1;
    	char c = getchar();
    	while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * f;
    }
    
  • 相关阅读:
    [工具-002]把png图片转换成ico图标
    软件攻城狮究级装B指南
    [翔哥高手无敌之路]0-002.如何提取apk中的信息?
    [Python基础]003.语法(2)
    [COCOS2DX-LUA]0-001.利用ClippingNode实现放大镜功能
    [书籍分享]0-005.淘宝技术这十年
    [翔哥高手无敌之路]0-001.项目有多余资源?那就删除吧!
    [Python基础]002.语法(1)
    [Objective-C] 001_Hello Objective-C
    DB2 pureXML 动态编程组合拳:iBatis+BeanUtils+JiBX
  • 原文地址:https://www.cnblogs.com/Bn_ff/p/13225513.html
Copyright © 2011-2022 走看看