codeforces round 396 div2 E Mahmoud and a xor trip 按位操作+dp子树内外

d every city with greater index as a finish. They want to know the total distance between all pairs of cities.
Input

The first line contains integer n (1 ≤ n ≤ 105) — the number of cities in Mahmoud and Ehab's country.

Then the second line contains n integers a1, a2, ..., an (0 ≤ ai ≤ 106) which represent the numbers attached to the cities. Integer ai is attached to the city i.

Each of the next n  -  1 lines contains two integers u and v (1  ≤  u,  v  ≤  n, u  ≠  v), denoting that there is an undirected road between cities u and v. It's guaranteed that you can reach any city from any other using these roads.

Output

Output one number denoting the total distance between all pairs of cities.

Examples
Input
3
1 2 3
1 2
2 3
Output
10
Input
5
1 2 3 4 5
1 2
2 3
3 4
3 5
Output
52
Input
5
10 9 8 7 6
1 2
2 3
3 4
3 5
Output
131
Note

A bitwise xor takes two bit integers of equal length and performs the logical xor operation on each pair of corresponding bits. The result in each position is 1 if only the first bit is 1 or only the second bit is 1, but will be 0 if both are 0 or both are 1. You can read more about bitwise xor operation here: https://en.wikipedia.org/wiki/Bitwise_operation#XOR.

In the first sample the available paths are:

  • city 1 to itself with a distance of 1,
  • city 2 to itself with a distance of 2,
  • city 3 to itself with a distance of 3,
  • city 1 to city 2 with a distance of ,
  • city 1 to city 3 with a distance of ,
  • city 2 to city 3 with a distance of .
The total distance between all pairs of cities equals 1 + 2 + 3 + 3 + 0 + 1 = 10.

题目描述:给定一棵有n(1<=n<=1e5)个节点的数树,每个节点有一个权值ai,两点u,v之间的距离为u、v路径上(包括u、v)所有节点的权值异或的结果,先问这棵树上所有满足1<u<=v<=n的节点对(u , v)的距离的总和是多少。
思路:如果想直接找异或的结果是比较困难的,所以要统计每一位上0和1的数量,dp0[0][i][u]表示u节点与其外子树所有节点距离在第i位上所有的0的个数,dp1[0][i][u]表示u节点与其外子树所有节点距离在第i位上所有的1的个数,dp0[1][i][u]表示u节点与其内子树所有节点距离在第i位上所有的0的个数,dp1[1][i][u]表示u节点与其内子树所有节点距离在第i位上所有的0的个数,想到这么统计,问题就比较好处理了,按照dp子树内外的方法得到dp值,然后统计加和就可以了,dp的转移见代码。

收获:1、异或的问题可以考虑按位统计
            2、树型dp不仅有单纯从叶子向根dp的方式,还有一种“dp子树内外”的办法
#pragma warning(disable:4786)
#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<cmath>
#include<string>
#include<sstream>
#include<bitset>
#define LL long long
#define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define lson l,m,x<<1
#define rson m+1,r,x<<1|1
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double PI = acos(-1.0);
const double eps=1e-6;
const int maxn = 1e5 + 5;
vector<int>G[maxn];
int  a[maxn] , bits[22][maxn];
LL dp0[2][22][maxn] , dp1[2][22][maxn] , x[100] , two[100];
void dfs(int cur , int fa)
{
    for(int i = 0 ; i < G[cur].size() ; i++){
        int nt = G[cur][i];
        if(nt == fa)        continue;
        for(int j = 1 ; j <= 20 ; j++){        //dp外子树
            if(bits[j][nt] == 0){
                dp1[0][j][nt] += (dp1[0][j][cur] + dp1[1][j][cur]);
                dp0[0][j][nt] += (dp0[0][j][cur] + dp0[1][j][cur]);
            }
            else{
                dp1[0][j][nt] += (dp0[0][j][cur] + dp0[1][j][cur]);
                dp0[0][j][nt] += (dp1[0][j][cur] + dp1[1][j][cur]);
            }
        }
        dfs(nt , cur);
        for(int j = 1 ; j <= 20 ; j++){     //dp内子树
            if(bits[j][cur] == 0){
                dp0[1][j][cur] += dp0[1][j][nt];
                dp1[1][j][cur] += dp1[1][j][nt];
            }
            else{
                dp0[1][j][cur] += dp1[1][j][nt];
                dp1[1][j][cur] += dp0[1][j][nt];
            }
        }
    }
    for(int i = 1 ; i <= 20 ; i++){
        if(bits[i][cur] == 0)
            ++dp0[1][i][cur];
        else
            ++dp1[1][i][cur];
    }
}
int main()
{
    int n , u , v;
    scanf("%d" , &n);
    for(int i = 1 ; i<= n ; i++){
        scanf("%d" , &a[i]);
        int x = a[i] , cnt = 1;
        while(x){
            bits[cnt++][i] += x % 2;
            x /= 2;
        }
    }
    for(int i = 0 ; i < n - 1 ; i++){
        scanf("%d %d" , &u , &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1 , -1);
    for(int i = 1 ; i <= 20 ; i++){
        for(int j = 1 ; j <= n ; j++){
            x[i] += dp1[0][i][j] + dp1[1][i][j];
        }
    }
    LL ans = 0;
    two[0] = 1;
    for(int i = 1 ; i <= 60 ; i++){
        two[i] = 2LL * two[i - 1];
        ans += (x[i] % 2 ) * two[i - 1];
        x[i + 1] = x[i + 1] + x[i] / 2;
    }
    printf("%lld\n",ans);
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值