CF766 E. Mahmoud and a xor trip [预处理][树形dp]

题解:

二营长!你他娘的意大利炮呢?

dp[i][j][0]: 从i,跋涉到以i为根的子树的每一个节点,在第j个数位上一共产生了多少个0。

dp[i][j][1]: 从i,跋涉到以i为根的子树的每一个节点,在第j个数位上一共产生了多少个1。

 

转移式:(cur为i的儿子,t = (a[i]>>j)&1)

dp[i][j][0^t] += dp[cur][j][0];

dp[i][j][1^t] += dp[cur][j][1];

初始化:

dp[i][j][0] = (t==0);

dp[i][j][1] = (t==1);

跑一遍dfs,意大利炮充能[MAX]!

我们接下来求以节点x为rank最小点的路径给答案的贡献。

这个地方需要维护,关于x的儿子のdp数组的前缀和。

而预处理时的转移式,恰好做到了这一点。

所以,加上一个式子,在dfs过程,就能计算答案。

code:

#include <iostream>
#include <vector>
#include <cstdio>
using namespace std;
typedef long long LL;
const int NICO = 100000 + 10;
int n, a[NICO];
vector<int> vec[NICO];
int dp[NICO][31][2];LL res;
void dfs(int x, int par)
{
    for(int i=0;i<30;i++)
    {
        if((1<<i)&a[x]) dp[x][i][1] = 1;
        else dp[x][i][0] = 1;
    }
    for(int i=0;i<vec[x].size();i++)
    {
        int cur = vec[x][i];
        if(cur == par) continue;
        dfs(cur, x);
        for(int j=0;j<30;j++)
        {
            res += ((LL)dp[cur][j][0]*dp[x][j][1] + (LL)dp[cur][j][1]*dp[x][j][0]) << j;//请机智の读者自行思考该式。
            int t = (a[x]>>j)&1;
            dp[x][j][0^t] += dp[cur][j][0];
            dp[x][j][1^t] += dp[cur][j][1];
        }
    }
}
int main()
{
    scanf("%d", &n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d", &a[i]);
        res += a[i]; //长度为0的路径还没考虑!
    }
    for(int i=1;i<n;i++)
    {
        int u, v;
        scanf("%d %d", &u, &v);
        vec[u].push_back(v);
        vec[v].push_back(u);
    }
    dfs(1, 0);
    cout << res << endl;
}

比赛的时候,意识到了这题得从每一个数位的角度来考虑,但被两点之间的公共祖先怼得想咬人!(◍ ૢ´꒳`◍ ૢ)

2333333。总之,这个题的预处理过程挺值得回味的。看来,举步维艰之时架好意大利炮,的确能扭转战局啊!

原文地址:https://www.cnblogs.com/RUSH-D-CAT/p/6395007.html