CodeForces - 766E (树形dp+二进制)

C - Mahmoud and a xor trip

 CodeForces - 766E

补题的时候碰见的一道题目,感觉思路非常巧妙就记录下来了。 

题目大意:给出n个点,每个点有一个点权,求所有路径的异或和,其中起点必须小于终点。

解题思路:我第一反应感觉有点像树分治,但是我不会,如果我们把每一个点权拆成二进制位,把答案分开计算也能得到最终答案。

如果我们每个点的值只有0或者1的话,让我们求异或和位1的路径数目,我们肯定会算。

以dp[i][0]表示以i为端点异或和0的数目,dp[i][1]表示以i为端点异或和为1的数目。

如何转移。

如果当前点的值为1,那么dp[u][1]=dp[u][1]+dp[v][0] dp[u][0]=dp[u][0]+dp[v][1];

同理当前点为0.。。。

其中v为u的一个子节点。

那么现在当前点不为1,是有多个0,1,组合而成,就进行logai次就可以了。。。

太妙了。。。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define sca(x) scanf("%d",&x)
#define pb(x) push_back(x)
#define N 100005

vector<int>V[N];
int dp[N][2];
LL ans;
int a[N];

void dfs(int u,int z,int fa)
{
    int pos=(a[u]&(1<<z))>>z;
    dp[u][0]=dp[u][1]=0;
    dp[u][pos]=1;
    for(int i=0; i<V[u].size(); i++)
    {
        int v=V[u][i];
        if(v==fa)continue;
        dfs(v,z,u);
        ans+=(dp[u][0]*dp[v][1]+dp[u][1]*dp[v][0])*(1LL<<z);
        if(pos)
        {
            dp[u][1]+=dp[v][0];
            dp[u][0]+=dp[v][1];
        }
        else
        {
            dp[u][1]+=dp[v][1];
            dp[u][0]+=dp[v][0];
        }
    }
}

int main()
{
    int n;
    sca(n);
    for(int i=1; i<=n; i++)sca(a[i]),ans+=a[i];
    for(int i=1; i<n; i++)
    {
        int x,y;
        sca(x),sca(y);
        V[x].pb(y);
        V[y].pb(x);
    }
    for(int i=0;i<21;i++)dfs(1,i,-1);
    cout<<ans<<endl;
}
/*
4
1 2 3 4
1 2
2 3
2 4
*/

猜你喜欢

转载自blog.csdn.net/weixin_40894017/article/details/88712697
今日推荐