首页 > 其他分享 >空夜 [换根DP]

空夜 [换根DP]

时间:2024-11-09 14:57:38浏览次数:1  
标签:int sum 空夜 DP 答案 换根 dp 为根

空夜

Description

给定 \(n\) 个节点的树,每个点有点权 \(a_i\),对于每个 \(i\),求出 \(\sum_{j} \lfloor \frac{a_i}{2^{dis(i,j)}} \rfloor\)。

\(dis(i,j)\) 表示 \(i\) 到 \(j\) 的树上最短路径。

Solution

  • 对于每个 \(i\) 都要求答案,等价于求以 \(i\) 为根的树的答案,可以想到 换根DP。

  • 套路地,我们先 树形DP 求出以 \(1\) 为根的树的答案,然后再考虑换根。

  • 设 \(f_u\) 表示以 \(u\) 为根的子树的答案,\(f_u=a_u+\sum (f_v\div 2)\),\(v\) 是 \(u\) 的儿子。但这样是对的吗?由于有下取整,所以我们直接把 \(f_v\) 除以二显然是会把答案算大的。

  • 我们从二进制上考虑,除以二下取整的本质是什么。显然是把该二进制右移一位,把第一位舍去了,那我们是不是可以记录这个子树的答案的第一位一共有多少个是 \(1\)。

  • 但是只记录第一位转移不了,于是我们记录 \(g_{i,j}\) 表示以 \(i\) 为根的子树的答案的第 \(j\) 位有多少个 \(1\)。转移就是,\(g_{u,i}=\sum g_{v,i+1}\)。

  • 那这时候 \(f\) 的转移就是 \(f_u=a_u+\sum((f_v-g_{v,1})\div 2)\)。

  • 换根的转移比较简单,具体见代码。

Code

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
bool START_MEMORY;

const int N = 3e5 + 5, LogN = 40;

int n;
ll a[N], pow2[40];
ll dp[N], cnt[N][LogN]; // dp[u] 表示以 u 为根的子树的答案,cnt[i][j] 表示以 i 为根,总贡献的二进制第 j 位有多少个 1.

vector <int> G[N];

void dfs1(int u, int fa) {
    dp[u] = a[u];
    for (int v : G[u]) {
        if (v == fa) continue;
        dfs1(v, u);
        dp[u] += (dp[v] - cnt[v][0]) / 2ll;
        for (int i = 0; i <= 32; i++) cnt[u][i] += cnt[v][i + 1];
    }
}

void dfs2(int u, int fa) {
    for (int v : G[u]) {
        if (v == fa) continue;
        dp[v] += (dp[u] - (dp[v] - cnt[v][0]) / 2ll - (cnt[u][0] - cnt[v][1])) / 2;
        for (int i = 0; i <= 32; i++) cnt[v][i] += cnt[u][i + 1] - cnt[v][i + 2];
        dfs2(v, u);
    }
}

void Solve() {
    pow2[0] = 1;
    for (int i = 1; i <= 35; i++) pow2[i] = pow2[i - 1] * 2;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        for (int j = 0; j <= 32; j++) {
            if (a[i] & pow2[j]) cnt[i][j] = 1;
        }
    }
    int u, v;
    for (int i = 1; i < n; i++) {
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs1(1, 0);
    dfs2(1, 0);
    for (int i = 1; i <= n; i++) cout << dp[i] << " ";
    cout << "\n";
}

bool END_MEMORY;
int main() {
    // freopen("sora.in", "r", stdin);
    // freopen("sora.out", "w", stdout);
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);

    Solve();

    cerr << "Time: " << 1000 * clock() / CLOCKS_PER_SEC << " ms\n";
    cerr << "Memory: " << fixed << setprecision(3) << double(&END_MEMORY - &START_MEMORY) / 1024 << " KB\n";
    return 0;
}

标签:int,sum,空夜,DP,答案,换根,dp,为根
From: https://www.cnblogs.com/chenwenmo/p/18536820

相关文章

  • RLGF无人机深度强化学习任务的通用训练框架(SAC, DQN, DDQN, PPO, Dueling DQN, DDPG)
    RLGF是一个通用的训练框架,适用于无人机的深度强化学习任务。该框架集成了多种主流的深度强化学习算法,包括SAC(SoftActor-Critic)、DQN(DeepQ-Network)、DDQN(DoubleDeepQ-Network)、PPO(ProximalPolicyOptimization)、DuelingDQN(决斗深度Q网络)以及DDPG(DeepDeterministicPo......
  • Android13 修改设备的density(dpi)
    DPIDPI,全称DotsPerInch,是一个衡量屏幕密度的关键指标。其中,Inch(英寸)作为物理单位,在任何设备上的大小都是恒定不变的。因此,DPI具体指的是在一英寸的物理长度内所能容纳的像素点(Dot)数量。例如,160DPI的屏幕意味着在一英寸的长度内包含160个像素点,而320DPI的屏幕则表明一英寸......
  • Air780E软件指南:UDP应用示例
    一、UDP概述UDP(用户数据报协议,UserDatagramProtocol)是一种无连接的、不可靠的传输层协议,主要用于实现网络中的快速通讯。以下是UDP通讯的主要特点:1.1无连接通讯:UDP在发送数据之前不需要建立连接,这大大减少了通讯的延迟。发送方只需将数据包封装成UDP报文,并附上目的地址......
  • 网络编程(一):UDP socket api => DatagramSocket & DatagramPacket
    目录1.TCP和UDP1.1TCP/UDP的区别1.1.1有连接vs无连接 1.1.2可靠传输vs不可靠传输 1.1.3面向字节流vs面向数据报1.1.4全双工vs半双工2.UDPsocketapi2.1DatagramSocket2.1.1构造方法2.1.2receive/send/close2.2DatagramPacket2.2.1......
  • 换根 DP
    树形DP中的换根DP问题又被称为二次扫描,通常需要求以每个点为根时某个式子的答案。这一类问题通常需要遍历两次树,第一次遍历先求出以某个点(如\(1\))为根时的答案,在第二次遍历时考虑由根为\(u\)转化为根为\(v\)时答案的变化(换根)。这个变化往往分为两部分,\(v\)子树外的点到......
  • 第一天打卡,udp协议
    今天学了udp协议基础,udp协议是一种无连接的网络协议,提供一种简单的方式来输送数据。发送:要用到的方法封装在InetAddress类中,其中DatagramSocket对象ds相当于快递员身份,不传递参数值的话会随机生成端口,进行输送快递(数据),快递的身份由DatagrampPacket对象充当,把东西打包。其中的......
  • [At_dp_w] Intervals & [At_dp_x] Tower
    两道题都很好Intervals给定\(m\)条规则形如\((l_i,r_i,a_i)\)​,对于一个01串,其分数的定义是:对于第\(i\)条规则,若该串在\([l_i,r_i]\)中至少有一个1,则该串的分数增加\(a_i\)你需要求出长度为\(n\)的01串中的最大分数\(1\len,m\le2\times10^5,|a_i|\le10^9\)......
  • Leetcode 474 dp数组讲解和滚动数组优化
    474.一和零​ ​dp[i][w1][w2]:DP数组的集合:考虑前i个物品包括第i个,满足背包重量不超过[w1][w2]的所有集合把输入数组中的每个string当作是一个物品,其重量分别为string中0和1的个数属性:价值每个物品的价值是1因为我们求最大子集的个数,一个字符串对子集个数贡献的......
  • 插入类DP
    对于这类题目的特征:1.排列性质2.\(100\len\le5\times10^3\)3.答案和排列的上升下降的突变点有关套路:按照某个从小到大的顺序插入,有了这个顺序就能算贡献或者方案数贡献往往提前计算,存在每个元素有新开一段、合并两端、连在某一段后面的不同方案有的允许了\(A\),\(W\),\(......
  • 大模型--训练 加速之 数据并行(DP, DDP与ZeRO)-上-12
    目录1.参考2.总结3.分布式数据并行(DDP)4.总结1.参考https://zhuanlan.zhihu.com/p/6171339712.总结以GoogleGPipe为代表的流水线并行范式,当模型太大,一块GPU放不下时,流水线并行,将模型的不同层放到不同的GPU上,通过切割mini-batch实现对训练数据的流水线处理,提升GPU计算......